Introdução
Rust e C++ são as duas linguagens de programação de sistemas mais relevantes da atualidade. C++, com mais de 40 anos de história, é a base de sistemas operacionais, game engines, navegadores, bancos de dados e praticamente toda infraestrutura crítica de software. Rust, lançada em 2015, foi criada explicitamente para resolver os problemas de segurança de memória que assolam projetos C++ há décadas.
A questão não é se Rust é “melhor” que C++ — ambas produzem código com performance comparável. A questão é se as garantias de segurança do Rust justificam a transição para projetos novos ou a reescrita gradual de projetos existentes.
Este artigo é para desenvolvedores C++ considerando Rust, e para desenvolvedores Rust que precisam entender como as duas linguagens se comparam em profundidade técnica.
Tabela Comparativa
| Aspecto | Rust | C++ |
|---|---|---|
| Segurança de memória | Garantida em tempo de compilação | Responsabilidade do programador |
| Modelo de memória | Ownership + Borrowing | RAII + smart pointers (opt-in) |
| Undefined Behavior | Impossível em safe Rust | Comum e difícil de detectar |
| Build system | Cargo (unificado) | CMake, Meson, Bazel, Make (fragmentado) |
| Performance | Equivalente a C++ | Referência de performance |
| Generics | Monomorphization + Traits | Templates (Turing-complete) |
| Herança | Sem herança (composição via Traits) | Herança múltipla, classes virtuais |
| ABI estável | Não (exceto via C ABI) | Não (mas mais estabelecido) |
| Ecossistema | crates.io, crescendo rápido | Imenso, mas fragmentado |
| Padrão da linguagem | Editions (2015, 2018, 2021, 2024) | ISO C++11/14/17/20/23/26 |
Segurança de Memória: O Diferencial Fundamental
A Microsoft estima que 70% das vulnerabilidades de segurança em seus produtos são causadas por erros de memória em código C/C++. O Google reporta números similares para o Chromium. Rust foi projetada para eliminar essas classes inteiras de bugs.
Use-After-Free em C++
#include <iostream>
#include <vector>
#include <string>
int main() {
std::vector<std::string> nomes = {"Alice", "Bob", "Carol"};
std::string& referencia = nomes[0];
nomes.push_back("Dave"); // Pode realocar o vetor!
// Undefined behavior: referencia pode apontar para memória inválida
std::cout << referencia << std::endl;
return 0;
}
Este código compila sem warnings na maioria dos compiladores, mas contém undefined behavior. O push_back pode realocar o vetor, invalidando referencia.
O Equivalente em Rust Não Compila
fn main() {
let mut nomes = vec!["Alice".to_string(), "Bob".to_string(), "Carol".to_string()];
let referencia = &nomes[0]; // empréstimo imutável
nomes.push("Dave".to_string()); // ERRO: não pode mutar enquanto emprestado
println!("{}", referencia);
}
error[E0502]: cannot borrow `nomes` as mutable because it is also
borrowed as immutable
--> src/main.rs:4:5
|
3 | let referencia = &nomes[0];
| ----- immutable borrow occurs here
4 | nomes.push("Dave".to_string());
| ^^^^^ mutable borrow occurs here
5 | println!("{}", referencia);
| ---------- immutable borrow later used here
O borrow checker do Rust detecta o problema em tempo de compilação. Para entender melhor este erro, veja nossa página sobre o erro E0382: uso após move.
RAII vs Ownership: Filosofias Diferentes
Ambas as linguagens usam RAII (Resource Acquisition Is Initialization), mas de formas diferentes.
C++ com Smart Pointers
#include <memory>
#include <iostream>
class Recurso {
std::string nome_;
public:
explicit Recurso(std::string nome) : nome_(std::move(nome)) {
std::cout << "Criando: " << nome_ << "\n";
}
~Recurso() {
std::cout << "Destruindo: " << nome_ << "\n";
}
void usar() const { std::cout << "Usando: " << nome_ << "\n"; }
};
void processar() {
auto r1 = std::make_unique<Recurso>("Principal");
r1->usar();
// Transferência de ownership (move semantics)
auto r2 = std::move(r1);
// r1 agora é nullptr — mas COMPILA se você usar r1!
// r1->usar(); // Undefined behavior em runtime
r2->usar();
} // r2 destruído automaticamente aqui
int main() {
processar();
return 0;
}
Rust com Ownership
struct Recurso {
nome: String,
}
impl Recurso {
fn new(nome: &str) -> Self {
println!("Criando: {nome}");
Recurso { nome: nome.to_string() }
}
fn usar(&self) {
println!("Usando: {}", self.nome);
}
}
impl Drop for Recurso {
fn drop(&mut self) {
println!("Destruindo: {}", self.nome);
}
}
fn processar() {
let r1 = Recurso::new("Principal");
r1.usar();
// Transferência de ownership (move)
let r2 = r1;
// r1.usar(); // ERRO DE COMPILAÇÃO: value used after move
r2.usar();
} // r2 destruído automaticamente aqui
fn main() {
processar();
}
A diferença crucial: em C++, usar um unique_ptr após std::move compila mas causa undefined behavior. Em Rust, o compilador impede o uso após move com um erro claro.
Para se aprofundar em ownership e borrowing, veja nosso tutorial de ownership e borrowing.
Exemplo Prático: Processamento Paralelo
C++ com <thread> e <mutex>
#include <iostream>
#include <thread>
#include <mutex>
#include <vector>
#include <numeric>
int main() {
std::vector<int> dados(1'000'000);
std::iota(dados.begin(), dados.end(), 1);
long long soma_total = 0;
std::mutex mtx;
const int num_threads = 4;
const int chunk = dados.size() / num_threads;
std::vector<std::thread> threads;
for (int t = 0; t < num_threads; ++t) {
int inicio = t * chunk;
int fim = (t == num_threads - 1) ? dados.size() : (t + 1) * chunk;
threads.emplace_back([&dados, &soma_total, &mtx, inicio, fim]() {
long long soma_local = 0;
for (int i = inicio; i < fim; ++i) {
soma_local += dados[i];
}
std::lock_guard<std::mutex> lock(mtx);
soma_total += soma_local;
});
}
for (auto& t : threads) {
t.join();
}
std::cout << "Soma: " << soma_total << std::endl;
return 0;
}
Rust com Rayon
use rayon::prelude::*;
fn main() {
let dados: Vec<i64> = (1..=1_000_000).collect();
let soma_total: i64 = dados.par_iter().sum();
println!("Soma: {soma_total}");
}
Rust com Rayon transforma código sequencial em paralelo com uma única mudança (.par_iter() em vez de .iter()). O compilador garante que não há data races — se o código compila, é thread-safe.
Em C++, o programador precisa gerenciar threads, mutexes e garantir manualmente que não há condições de corrida.
Comparação de Performance
Benchmarks (Compilados com Otimização Máxima)
| Benchmark | Rust | C++ | Diferença |
|---|---|---|---|
| Fibonacci(45) | 3,2s | 3,1s | ~3% C++ |
| Sort 100M inteiros | 4,8s | 4,6s | ~4% C++ |
| Parse JSON 1GB | 0,8s (serde) | 0,7s (simdjson) | ~14% C++ |
| Regex 500MB | 0,28s (regex) | 0,30s (RE2) | ~7% Rust |
| HTTP req/s | 850k (axum) | 900k (drogon) | ~6% C++ |
| Compilação (projeto médio) | 45s | 120s (CMake+ninja) | ~63% Rust |
A performance é essencialmente equivalente. Diferenças menores dependem do benchmark específico, otimizações do compilador e bibliotecas usadas. A vantagem do Rust é conseguir essa performance com garantias de segurança.
Tempo de Compilação
Curiosamente, Rust frequentemente compila mais rápido que C++ em projetos grandes, graças ao Cargo e ao sistema de módulos mais eficiente. C++ sofre com o modelo de includes baseado em texto e tempos de link lentos.
Build Systems: Cargo vs o Caos
Uma das maiores frustrações em C++ é o ecossistema de build fragmentado:
# C++: varia por projeto
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build --parallel
# Ou: meson setup build && ninja -C build
# Ou: bazel build //...
# Ou: make -j$(nproc)
# Rust: sempre o mesmo
cargo build --release
cargo test
cargo doc
O Cargo é unanimemente considerado uma das maiores vantagens do Rust. Gerenciamento de dependências, compilação, testes, documentação e publicação — tudo em uma ferramenta.
Quando Usar C++
Escolha C++ quando:
- O projeto já existe em C++: reescrever bilhões de linhas não é prático
- Game engines: Unreal Engine, Unity (runtime), Godot — o ecossistema é C++
- ABI estável é necessário: interoperabilidade com bibliotecas existentes
- A equipe conhece C++ profundamente: expertise conta muito
- Plataformas com toolchains limitados: alguns embarcados só têm compilador C/C++
Quando Usar Rust
Escolha Rust quando:
- Projeto novo de sistemas: sem legado para manter compatibilidade
- Segurança é prioridade: criptografia, kernels, navegadores, infraestrutura
- Concorrência pesada: o compilador elimina data races
- Build system importa: Cargo vs CMake não tem comparação
- Atração de talentos: Rust é consistentemente a linguagem “mais amada”
Tendências: Rust no Mundo C++
O movimento de adoção de Rust em projetos tradicionalmente C++ é real:
- Linux Kernel: aceita módulos em Rust desde 2022
- Android: mais de 1 milhão de linhas de Rust no AOSP
- Windows: Microsoft usa Rust em componentes do kernel
- Chromium: Google começou a adotar Rust no navegador
- NSA e Casa Branca: recomendam oficialmente linguagens memory-safe
Conclusão e Recomendação
Para projetos novos de sistemas em 2026, recomendamos Rust. A performance é equivalente a C++, e as garantias de segurança de memória eliminam classes inteiras de bugs que custam bilhões de dólares à indústria. O ecossistema (Cargo, crates.io, documentação) é mais moderno e produtivo.
Para projetos C++ existentes, a abordagem pragmática é adotar Rust gradualmente: novos componentes em Rust, interoperabilidade via FFI (C ABI), e reescrita incremental dos módulos mais críticos. Não tente reescrever tudo de uma vez.
Se você é desenvolvedor C++, aprender Rust vai torná-lo um programador melhor mesmo em C++ — os conceitos de ownership e borrowing vão mudar como você pensa sobre gerenciamento de recursos.
Veja Também
- Entendendo o Erro E0382: Uso Após Move — O erro mais comum para quem vem de C++
- Tutorial: Ownership e Borrowing — O conceito central do Rust
- Rust vs Go: Qual Escolher em 2026 — Compare com outra linguagem moderna
- Rust vs C: Modernizando Código de Sistemas — Compare com o ancestral do C++
- Cross-Compilation em Rust — Compile para diferentes plataformas
- Glossário Rust — Termos como RAII, ownership e borrow checker