Rust vs C: Qual Escolher em 2026?

Rust vs C: compare segurança de memória, performance, FFI, ecossistema e migração. Decida quando escolher cada linguagem e como adotar Rust em código C.

Rust ou C: resposta rápida

Para um projeto novo de sistemas em 2026, escolha Rust quando segurança de memória, concorrência e manutenção forem prioridades; escolha C quando você depender de uma plataforma sem bom suporte a Rust, de uma ABI C estável ou de uma base C madura. As duas linguagens podem entregar desempenho de baixo nível. A diferença principal é que Rust transfere várias verificações de memória e concorrência para o compilador, enquanto C dá ao programador controle mais direto — e também a responsabilidade por evitar essas falhas.

CenárioEscolha mais provávelMotivo
Serviço de rede, agente ou infraestrutura novaRustSegurança de memória, concorrência e ecossistema moderno
Firmware em alvo com suporte Rust incompletoCToolchain e bibliotecas do fabricante
Extensão segura para um sistema C existenteRust + CMigração gradual por FFI, sem reescrita total
Biblioteca com ABI pública consumida por muitas linguagensC ou Rust com ABI CCompatibilidade binária previsível
Equipe experiente em C mantendo produto estávelC, inicialmenteCusto e risco de migração podem superar o benefício
Componente novo exposto a entrada não confiávelRustReduz classes inteiras de vulnerabilidades de memória

C é, sem exagero, uma das linguagens mais influentes da história da computação. Criada em 1972 por Dennis Ritchie, ela é a base de sistemas operacionais, bancos de dados, runtimes e infraestrutura. Rust chegou décadas depois para atender muitos dos mesmos domínios com garantias de segurança em tempo de compilação e sem garbage collector.

Este comparativo é para quem pesquisa Rust vs C, C vs Rust ou simplesmente Rust ou C para um projeto. A seguir, você verá segurança de memória, FFI, performance, ferramentas e um critério prático de decisão.

Tabela Comparativa

AspectoRustC
Ano de criação20151972
Segurança de memóriaGarantida pelo compiladorResponsabilidade total do programador
Undefined BehaviorImpossível em safe RustCentenas de formas possíveis
Gerenciamento de memóriaOwnership (automático, sem GC)Manual (malloc/free)
Sistema de tiposRico (enums, generics, traits)Básico (structs, unions, typedef)
Build systemCargo (unificado)Make, CMake, Meson (fragmentado)
Gerenciamento de dependênciascrates.io (integrado)Nenhum padrão (pkg-config, conan)
PerformanceEquivalente a CReferência de performance
ABINão estável (usa C ABI para FFI)ABI estável de fato
PortabilidadeMuito boa (LLVM)Máxima (compilador para tudo)
ConcorrênciaThread safety no compiladorSem garantias (pthreads manual)

Segurança de Memória: O Problema Fundamental

Segundo a NSA, Microsoft e Google, 60-70% de todas as vulnerabilidades de segurança em software de sistemas são causadas por erros de memória em C e C++. Vamos ver os erros mais comuns:

Buffer Overflow em C

#include <stdio.h>
#include <string.h>

void processar_entrada(const char *entrada) {
    char buffer[64];
    // PERIGO: strcpy não verifica limites!
    strcpy(buffer, entrada);
    printf("Processado: %s\n", buffer);
}

int main(void) {
    // Se entrada tiver mais de 63 chars, overflow!
    char entrada_longa[256];
    memset(entrada_longa, 'A', 255);
    entrada_longa[255] = '\0';

    processar_entrada(entrada_longa); // Buffer overflow!
    return 0;
}

O Equivalente Seguro em Rust

fn processar_entrada(entrada: &str) {
    // String em Rust sempre conhece seu tamanho — overflow é impossível
    let buffer: String = entrada.chars().take(64).collect();
    println!("Processado: {buffer}");
}

fn main() {
    let entrada_longa = "A".repeat(255);
    processar_entrada(&entrada_longa); // Seguro, trunca automaticamente
}

Use-After-Free em C

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void) {
    char *nome = malloc(32);
    if (!nome) return 1;
    strcpy(nome, "Rust Brasil");

    free(nome);

    // Use-after-free: comportamento indefinido!
    // Pode funcionar, crashar ou ser explorado
    printf("Nome: %s\n", nome);

    return 0;
}

Rust Impede Use-After-Free

fn main() {
    let nome = String::from("Rust Brasil");
    drop(nome); // Libera a memória explicitamente

    // ERRO DE COMPILAÇÃO: value used after move
    // println!("Nome: {nome}");
}

Double Free em C

#include <stdlib.h>

int main(void) {
    int *dados = malloc(100 * sizeof(int));
    if (!dados) return 1;

    free(dados);
    free(dados); // Double free: undefined behavior!

    return 0;
}

Em Rust, double free é estruturalmente impossível: cada valor tem exatamente um owner, e o drop é chamado automaticamente quando o owner sai de escopo. Não existe como liberar a mesma memória duas vezes.

FFI: Interoperabilidade Rust-C

Uma das maiores vantagens do Rust é sua interoperabilidade nativa com C via FFI (Foreign Function Interface). Isso permite adotar Rust gradualmente em projetos C existentes.

Chamando C a partir de Rust

// Declaração da função C
extern "C" {
    fn abs(n: i32) -> i32;
    fn strlen(s: *const std::ffi::c_char) -> usize;
}

fn main() {
    unsafe {
        let valor = abs(-42);
        println!("Valor absoluto: {valor}"); // 42

        let texto = std::ffi::CString::new("Olá Rust").unwrap();
        let tamanho = strlen(texto.as_ptr());
        println!("Tamanho: {tamanho}"); // 8
    }
}

Expondo Rust para C

// lib.rs — biblioteca Rust utilizável por código C
use std::ffi::{c_char, CStr, CString};
use std::ptr;

#[no_mangle]
pub extern "C" fn somar_vetor(dados: *const f64, tamanho: usize) -> f64 {
    if dados.is_null() || tamanho == 0 {
        return 0.0;
    }
    let slice = unsafe { std::slice::from_raw_parts(dados, tamanho) };
    slice.iter().sum()
}

#[no_mangle]
pub extern "C" fn criar_saudacao(nome: *const c_char) -> *mut c_char {
    let nome_str = unsafe {
        if nome.is_null() {
            return ptr::null_mut();
        }
        CStr::from_ptr(nome).to_str().unwrap_or("mundo")
    };

    let saudacao = format!("Olá, {nome_str}!");
    CString::new(saudacao)
        .map(|s| s.into_raw())
        .unwrap_or(ptr::null_mut())
}

#[no_mangle]
pub extern "C" fn liberar_string(s: *mut c_char) {
    if !s.is_null() {
        unsafe {
            drop(CString::from_raw(s));
        }
    }
}
/* main.c — código C chamando a biblioteca Rust */
#include <stdio.h>

extern double somar_vetor(const double *dados, size_t tamanho);
extern char *criar_saudacao(const char *nome);
extern void liberar_string(char *s);

int main(void) {
    double valores[] = {1.5, 2.5, 3.0, 4.0};
    double soma = somar_vetor(valores, 4);
    printf("Soma: %.1f\n", soma); /* Soma: 11.0 */

    char *msg = criar_saudacao("Comunidade");
    if (msg) {
        printf("%s\n", msg); /* Olá, Comunidade! */
        liberar_string(msg);
    }

    return 0;
}

Para compilar e linkar esses exemplos em diferentes plataformas, veja nosso guia de cross-compilation.

Exemplo Prático: Linked List

Uma linked list é um exercício clássico que demonstra as diferenças de gestão de memória.

C com malloc/free

#include <stdio.h>
#include <stdlib.h>

typedef struct No {
    int valor;
    struct No *proximo;
} No;

No *criar_no(int valor) {
    No *no = malloc(sizeof(No));
    if (!no) return NULL;
    no->valor = valor;
    no->proximo = NULL;
    return no;
}

void inserir(No **cabeca, int valor) {
    No *novo = criar_no(valor);
    if (!novo) return;
    novo->proximo = *cabeca;
    *cabeca = novo;
}

void imprimir(const No *cabeca) {
    const No *atual = cabeca;
    while (atual) {
        printf("%d -> ", atual->valor);
        atual = atual->proximo;
    }
    printf("NULL\n");
}

void liberar(No *cabeca) {
    while (cabeca) {
        No *temp = cabeca;
        cabeca = cabeca->proximo;
        free(temp);
    }
}

int main(void) {
    No *lista = NULL;
    inserir(&lista, 3);
    inserir(&lista, 2);
    inserir(&lista, 1);
    imprimir(lista);   /* 1 -> 2 -> 3 -> NULL */
    liberar(lista);    /* Esquecer essa linha = memory leak */
    return 0;
}

Rust com Box

type Link = Option<Box<No>>;

struct No {
    valor: i32,
    proximo: Link,
}

struct Lista {
    cabeca: Link,
}

impl Lista {
    fn new() -> Self {
        Lista { cabeca: None }
    }

    fn inserir(&mut self, valor: i32) {
        let novo = Box::new(No {
            valor,
            proximo: self.cabeca.take(),
        });
        self.cabeca = Some(novo);
    }

    fn imprimir(&self) {
        let mut atual = &self.cabeca;
        while let Some(no) = atual {
            print!("{} -> ", no.valor);
            atual = &no.proximo;
        }
        println!("None");
    }
}
// Drop é chamado automaticamente — sem memory leak possível

fn main() {
    let mut lista = Lista::new();
    lista.inserir(3);
    lista.inserir(2);
    lista.inserir(1);
    lista.imprimir(); // 1 -> 2 -> 3 -> None
} // lista é automaticamente liberada aqui, recursivamente

Em C, esquecer de chamar liberar() causa memory leak. Em Rust, a memória é liberada automaticamente quando lista sai de escopo.

Comparação de Performance

O que comparar em um benchmark

Não existe um número universal para provar que Rust ou C é mais rápido. As duas linguagens compilam para código nativo, permitem controlar layout e alocação e podem usar otimizações equivalentes. Um comparativo útil precisa testar a mesma tarefa, o mesmo algoritmo, entradas representativas e configurações de compilação equivalentes.

FatorComo comparar corretamente
Compilação otimizadaUse cargo build --release em Rust e flags de otimização equivalentes no compilador C
Algoritmo e estrutura de dadosMantenha a mesma complexidade e evite comparar bibliotecas com estratégias diferentes
AlocaçõesConte alocações, cópias e mudanças de capacidade, não apenas o tempo total
Bounds checksMeça antes de usar unsafe; o otimizador elimina muitas verificações quando consegue provar os limites
Bibliotecas externasRegistre versões e implementações, pois elas podem pesar mais que a linguagem
AmbienteFixe CPU, sistema, carga, amostras e warm-up; reporte a variação, não só a melhor execução

Na prática, Rust pode empatar, ganhar ou perder para C conforme a implementação. O argumento mais forte para Rust não é uma promessa de velocidade automática, mas combinar performance previsível com garantias que C normalmente busca por revisão, sanitizers e disciplina manual. Para medir seu próprio código, consulte o guia de otimização e profiling em Rust.

Rust no Linux Kernel

Desde 2022, o Linux kernel aceita contribuições em Rust. Isso representa uma mudança histórica:

  • Drivers de dispositivo estão sendo escritos em Rust
  • Subsistemas novos podem escolher Rust como linguagem
  • O projeto Rust for Linux mantém bindings seguros para as APIs do kernel
  • Linus Torvalds aprovou pessoalmente a integração

Isso não significa que o kernel será reescrito em Rust — o código C existente (30+ milhões de linhas) continuará sendo mantido. Mas novos módulos têm a opção de usar Rust para maior segurança.

Quando Usar C

Escolha C quando:

  • O projeto já é em C: manter consistência em bases de código existentes
  • ABI estável é obrigatório: bibliotecas que precisam ser linkadas por qualquer linguagem
  • Plataforma sem suporte Rust: microcontroladores muito pequenos, plataformas obscuras
  • Tamanho mínimo absoluto: C pode produzir binários menores (sem runtime Rust)
  • Padrão da indústria: MISRA C, AUTOSAR, POSIX strict

Quando Usar Rust

Escolha Rust quando:

  • Projeto novo de sistemas: sem legado para manter
  • Segurança de memória importa: infraestrutura, networking, criptografia
  • Concorrência é necessária: Rust garante thread safety em compilação
  • Produtividade do desenvolvedor: Cargo, enums, pattern matching, iteradores
  • Manutenção a longo prazo: refatoração segura, menos bugs em produção

Perguntas frequentes

Rust é mais rápido que C?

Rust e C podem atingir desempenho equivalente. Algoritmo, alocações, bibliotecas, flags de compilação e qualidade da implementação costumam afetar mais o resultado do que o nome da linguagem.

Rust pode substituir C?

Em muitos projetos novos, sim. Em sistemas C existentes, a abordagem mais segura costuma ser introduzir Rust em componentes novos ou mais expostos a falhas, mantendo o restante e integrando os dois lados por FFI.

Quando devo escolher C em vez de Rust?

C continua adequado quando o alvo só possui uma toolchain C madura, quando compatibilidade com código e padrões legados domina a decisão ou quando uma equipe experiente precisa manter um produto C estável sem um caso de negócio para migrar.

É possível usar Rust e C no mesmo projeto?

Sim. Rust pode chamar funções C e exportar funções com ABI C. A fronteira precisa documentar ponteiros, tamanhos, ownership, erros e quem libera cada recurso.

Rust é mais seguro que C?

Safe Rust impede classes comuns de erros de memória e data races em tempo de compilação. Código unsafe e FFI continuam exigindo auditoria, mas permitem concentrar essa responsabilidade em uma superfície menor.

Conclusão e Recomendação

Para novos projetos de sistemas em 2026, Rust é a escolha recomendada. Você obtém performance equivalente a C com segurança de memória garantida, um sistema de build moderno (Cargo), gerenciamento de dependências integrado e abstrações de alto nível que não custam nada em runtime.

Para projetos C existentes, a estratégia mais inteligente é adotar Rust nos módulos novos e manter o código C existente. A FFI entre Rust e C é excelente e permite migração gradual. Não reescreva tudo de uma vez — o kernel Linux está mostrando o caminho com adoção incremental.

Se você programa em C, aprender Rust vai torná-lo mais produtivo e consciente sobre segurança de memória. Muitos conceitos que em C exigem disciplina e atenção manual são automatizados pelo compilador do Rust.


Veja Também