Introdução
Rust e Swift são duas linguagens modernas que compartilham diversas similaridades: ambas priorizam segurança, possuem sistemas de tipos expressivos, usam enums com dados associados e favorecem tratamento explícito de erros. Porém, seus ecossistemas e casos de uso são significativamente diferentes.
Swift, criada pela Apple em 2014, é a linguagem padrão para desenvolvimento iOS, macOS, watchOS e tvOS. Nos últimos anos, expandiu para server-side com frameworks como Vapor e para desenvolvimento cross-platform com Swift on Server. Rust é uma linguagem de sistemas de propósito geral, usada de infraestrutura a WebAssembly, sem vínculos com nenhuma plataforma específica.
Este artigo é para desenvolvedores Swift curiosos sobre Rust, desenvolvedores Rust que trabalham no ecossistema Apple, e qualquer pessoa que queira entender como essas duas linguagens modernas se comparam.
Tabela Comparativa
| Aspecto | Rust | Swift |
|---|---|---|
| Gerenciamento de memória | Ownership + Borrow Checker | ARC (Automatic Reference Counting) |
| Ciclos de referência | Impossíveis (ownership linear) | Possíveis (weak/unowned necessários) |
| Performance | Equivalente a C/C++ | Próxima de C (ARC tem overhead) |
| Concorrência | async/await + Send/Sync (compile-time) | Swift Concurrency + actors (runtime) |
| Null safety | Option<T> | Optional (T?) |
| Pattern matching | match (exaustivo) | switch (exaustivo) |
| Generics | Monomorphization + Traits | Specialization + Protocols |
| Ecossistema | Cross-platform, crates.io | Apple-centric, Swift Package Manager |
| IDE | VS Code, RustRover, Neovim | Xcode, VS Code |
| Cross-platform | Excelente (Linux, Windows, macOS, Wasm) | Bom (Linux, macOS, Windows experimental) |
ARC vs Ownership: Modelos de Memória
A diferença mais fundamental entre Rust e Swift é como gerenciam memória.
Swift: Automatic Reference Counting
class Documento {
let titulo: String
var conteudo: String
init(titulo: String, conteudo: String) {
self.titulo = titulo
self.conteudo = conteudo
print("Criando: \(titulo)")
}
deinit {
print("Destruindo: \(titulo)")
}
}
func exemplo() {
let doc1 = Documento(titulo: "Relatório", conteudo: "...")
let doc2 = doc1 // Incrementa reference count (agora RC = 2)
// Ambos doc1 e doc2 apontam para o MESMO objeto
doc2.conteudo = "Atualizado"
print(doc1.conteudo) // "Atualizado" — mesmo objeto!
}
// RC chega a 0, deinit é chamado
// PERIGO: ciclos de referência
class No {
var proximo: No? // Strong reference
deinit { print("No destruído") }
}
func criarCiclo() {
let a = No()
let b = No()
a.proximo = b
b.proximo = a // Ciclo! Memory leak — deinit nunca é chamado
}
Rust: Ownership
struct Documento {
titulo: String,
conteudo: String,
}
impl Documento {
fn new(titulo: &str, conteudo: &str) -> Self {
println!("Criando: {titulo}");
Documento {
titulo: titulo.to_string(),
conteudo: conteudo.to_string(),
}
}
}
impl Drop for Documento {
fn drop(&mut self) {
println!("Destruindo: {}", self.titulo);
}
}
fn exemplo() {
let doc1 = Documento::new("Relatório", "...");
let doc2 = doc1; // MOVE — doc1 não é mais válido
// doc1.conteudo; // ERRO: value used after move
// Para compartilhar, use referências explícitas:
let doc3 = Documento::new("Outro", "...");
let ref1 = &doc3;
let ref2 = &doc3;
println!("{} {}", ref1.titulo, ref2.titulo); // OK: múltiplas referências imutáveis
}
// Ciclos de referência: impossíveis com ownership linear
// Se você precisar de grafos cíclicos, use Rc<RefCell<T>> explicitamente
A diferença chave: Swift usa contagem de referências automática (ARC), que tem overhead em runtime (incrementar/decrementar contadores atomicamente) e permite ciclos de referência que causam memory leaks. Rust usa ownership linear, que não tem overhead em runtime e torna ciclos de referência estruturalmente impossíveis no caso comum.
Enums e Pattern Matching: Surpreendentemente Similares
Ambas as linguagens têm enums com dados associados e pattern matching exaustivo:
Swift
enum Resultado<T> {
case sucesso(T)
case erro(String)
}
enum Forma {
case circulo(raio: Double)
case retangulo(largura: Double, altura: Double)
case triangulo(base: Double, altura: Double)
}
func area(_ forma: Forma) -> Double {
switch forma {
case .circulo(let raio):
return .pi * raio * raio
case .retangulo(let largura, let altura):
return largura * altura
case .triangulo(let base, let altura):
return base * altura / 2.0
}
}
let formas: [Forma] = [
.circulo(raio: 5.0),
.retangulo(largura: 3.0, altura: 4.0),
.triangulo(base: 6.0, altura: 3.0),
]
for forma in formas {
print("Área: \(area(forma))")
}
Rust
use std::f64::consts::PI;
enum Forma {
Circulo { raio: f64 },
Retangulo { largura: f64, altura: f64 },
Triangulo { base: f64, altura: f64 },
}
fn area(forma: &Forma) -> f64 {
match forma {
Forma::Circulo { raio } => PI * raio * raio,
Forma::Retangulo { largura, altura } => largura * altura,
Forma::Triangulo { base, altura } => base * altura / 2.0,
}
}
fn main() {
let formas = vec![
Forma::Circulo { raio: 5.0 },
Forma::Retangulo { largura: 3.0, altura: 4.0 },
Forma::Triangulo { base: 6.0, altura: 3.0 },
];
for forma in &formas {
println!("Área: {:.2}", area(forma));
}
}
A sintaxe é notavelmente similar. Desenvolvedores Swift se sentirão em casa com enums e pattern matching em Rust — a transferência de conhecimento é direta.
Concorrência: Actors vs Send/Sync
Swift Concurrency (Actors)
actor ContaBancaria {
private var saldo: Double
init(saldoInicial: Double) {
self.saldo = saldoInicial
}
func depositar(_ valor: Double) {
saldo += valor
}
func sacar(_ valor: Double) -> Bool {
guard saldo >= valor else { return false }
saldo -= valor
return true
}
func consultarSaldo() -> Double {
return saldo
}
}
func exemplo() async {
let conta = ContaBancaria(saldoInicial: 1000.0)
await conta.depositar(500.0)
let sucesso = await conta.sacar(200.0)
let saldo = await conta.consultarSaldo()
print("Saldo: \(saldo), Saque OK: \(sucesso)")
}
Rust com Tokio e Mutex
use std::sync::Arc;
use tokio::sync::Mutex;
struct ContaBancaria {
saldo: f64,
}
impl ContaBancaria {
fn new(saldo_inicial: f64) -> Self {
ContaBancaria { saldo: saldo_inicial }
}
fn depositar(&mut self, valor: f64) {
self.saldo += valor;
}
fn sacar(&mut self, valor: f64) -> bool {
if self.saldo >= valor {
self.saldo -= valor;
true
} else {
false
}
}
fn consultar_saldo(&self) -> f64 {
self.saldo
}
}
#[tokio::main]
async fn main() {
let conta = Arc::new(Mutex::new(ContaBancaria::new(1000.0)));
{
let mut c = conta.lock().await;
c.depositar(500.0);
}
let sucesso = {
let mut c = conta.lock().await;
c.sacar(200.0)
};
let saldo = {
let c = conta.lock().await;
c.consultar_saldo()
};
println!("Saldo: {saldo}, Saque OK: {sucesso}");
}
Swift actors são mais ergonômicos — o isolamento de dados é automático. Em Rust, você usa Arc<Mutex<T>> e gerencia locks manualmente, mas o compilador garante em tempo de compilação (via traits Send e Sync) que você não tem data races.
Comparação de Performance
Benchmarks
| Benchmark | Rust | Swift | Diferença |
|---|---|---|---|
| Fibonacci(45) | 3,2s | 3,8s | ~19% Rust |
| Sort 10M inteiros | 0,48s | 0,55s | ~15% Rust |
| ARC/Drop overhead | 0 (ownership) | ~5-15% (ARC) | Rust sem overhead |
| Parsing JSON 100MB | 0,08s (serde) | 0,15s (Codable) | ~47% Rust |
| HTTP req/s | 850k (axum) | 280k (Vapor) | ~3x Rust |
| Tamanho do binário | ~3 MB | ~8 MB | ~63% Rust |
| Memória idle (servidor) | ~5 MB | ~15 MB | ~67% Rust |
A principal vantagem de performance do Rust sobre Swift vem da ausência de ARC. Em código com muitas alocações e referências (como servidores web), a contagem de referências atômica do ARC adiciona overhead mensurável.
Cross-Platform: Onde Cada Uma Brilha
Swift
- Ecossistema Apple: iOS, macOS, watchOS, tvOS — tooling e integração incomparáveis
- SwiftUI: framework declarativo para interfaces nativas
- Server-side: Vapor e Hummingbird, mas adoção ainda pequena fora da Apple
- Linux: suporte oficial, mas ecossistema limitado
- Windows: suporte experimental, ainda não maduro
Rust
- Linux, macOS, Windows: suporte de primeira classe em todas
- WebAssembly: melhor suporte para Wasm entre linguagens de sistemas
- Embarcados: suporte crescente para ARM, RISC-V, sem_std
- Android (NDK): boa integração via C ABI
- iOS: possível via C ABI, mas menos ergonômico que Swift
Para configurar Rust no macOS, veja nosso guia de instalação para macOS.
Quando Usar Swift
Escolha Swift quando:
- Desenvolvimento Apple: iOS, macOS, watchOS — sem contestação
- SwiftUI/UIKit: interfaces nativas para plataformas Apple
- Server-side no ecossistema Apple: se a equipe já domina Swift
- Prototipagem com Playgrounds: exploração rápida de ideias
- Kotlin Multiplatform não atende: Swift é mais natural para devs Apple
Quando Usar Rust
Escolha Rust quando:
- Cross-platform real: o mesmo código em Linux, macOS, Windows e Wasm
- Performance máxima: sem overhead de ARC, controle total de memória
- Infraestrutura e sistemas: servidores, CLIs, proxies, bancos de dados
- Componentes de alta performance para iOS/macOS: via FFI como biblioteca C
- WebAssembly: módulos Wasm de alta performance
Conclusão e Recomendação
Para desenvolvimento no ecossistema Apple, Swift é a escolha óbvia e correta. Xcode, SwiftUI, e a integração nativa com as APIs da Apple não têm equivalente em Rust. Mesmo para server-side, se sua equipe é de Swift, usar Vapor faz sentido.
Para projetos cross-platform, infraestrutura e alta performance, Rust é a melhor escolha. Se você precisa que o mesmo código rode eficientemente em Linux, macOS, Windows e WebAssembly, Rust tem suporte muito superior.
Se você é desenvolvedor Swift, aprender Rust será mais fácil do que para a maioria dos programadores — os conceitos de enums, pattern matching, generics e tratamento explícito de erros são muito similares. A maior novidade será o ownership system, que substitui o ARC com que você já está acostumado.
A combinação ideal para projetos Apple que precisam de componentes de alta performance é: Swift para a UI e lógica de aplicação, Rust para bibliotecas de processamento, conectados via C ABI.
Veja Também
- Instalação do Rust no macOS — Configure Rust no ambiente Apple
- Rust vs C++: Segurança sem Sacrificar Performance — Outra linguagem de sistemas
- Rust vs Kotlin: Server-Side e Mobile — Compare com outra linguagem mobile moderna
- Rust vs Go: Qual Escolher em 2026 — Para decisões de backend cross-platform
- Tutorial: Ownership e Borrowing — Entenda o modelo de memória do Rust
- Glossário Rust — Termos como ARC, ownership, traits e enums