E0308: Tipos Incompatíveis no Rust

Como resolver o erro E0308 do Rust: tipos incompatíveis (mismatched types). Veja os casos mais comuns como String vs &str, i32 vs usize e como converter entre tipos.

E0308: Tipos Incompatíveis

O erro E0308 é um dos mais frequentes no Rust e ocorre quando o compilador espera um tipo, mas encontra outro. O sistema de tipos do Rust é forte e estático — não existe conversão implícita entre tipos, mesmo entre i32 e i64 ou String e &str.

A Mensagem de Erro

error[E0308]: mismatched types
 --> src/main.rs:3:20
  |
3 |     let nome: &str = String::from("Rust");
  |               ----   ^^^^^^^^^^^^^^^^^^^^^ expected `&str`, found `String`
  |               |
  |               expected due to this

Outra variação comum:

error[E0308]: mismatched types
 --> src/main.rs:5:18
  |
5 |     let indice: i32 = vec.len();
  |                 ---   ^^^^^^^^^ expected `i32`, found `usize`
  |                 |
  |                 expected due to this

O Que Significa

O Rust exige que os tipos sejam exatamente compatíveis. Diferente de linguagens como JavaScript ou Python, não há coerção automática entre tipos. Isso previne bugs sutis como:

  • Perda de precisão ao converter f64 para f32
  • Overflow ao converter i64 para i32
  • Problemas de memória ao confundir String (owned) com &str (referência)

Os casos mais comuns deste erro são:

  1. String vs &str — O par mais confuso para iniciantes
  2. i32 vs usize — Retorno de .len() é usize
  3. Option<T> vs T — Esqueceu de desempacotar o Option
  4. Result<T, E> vs T — Esqueceu de tratar o Result
  5. () vs outro tipo — Função sem retorno explícito retorna ()

Código com Erro

String vs &str

fn saudacao(nome: &str) -> String {
    format!("Olá, {}!", nome)
}

fn main() {
    let nome = String::from("Rust");
    let msg: &str = saudacao(&nome);  // ERRO: esperava &str, recebeu String
}

i32 vs usize

fn main() {
    let numeros = vec![1, 2, 3, 4, 5];
    let indice: i32 = numeros.len();  // ERRO: len() retorna usize
}

Option vs valor direto

fn main() {
    let numeros = vec![10, 20, 30];
    let primeiro: &i32 = numeros.get(0);  // ERRO: get() retorna Option<&i32>
}

Como Resolver

Solução 1: String e &str — Conversões Corretas

// &str para String
let owned: String = "texto".to_string();
let owned: String = String::from("texto");
let owned: String = "texto".to_owned();

// String para &str
let s = String::from("texto");
let referencia: &str = &s;        // coerção automática (deref)
let referencia: &str = s.as_str(); // explícito

Corrigindo o exemplo anterior:

fn saudacao(nome: &str) -> String {
    format!("Olá, {}!", nome)
}

fn main() {
    let nome = String::from("Rust");
    let msg: String = saudacao(&nome);  // Tipo correto agora
    println!("{}", msg);
}

Solução 2: Conversões Numéricas com as ou try_into()

fn main() {
    let numeros = vec![1, 2, 3, 4, 5];

    // Opção 1: use o tipo correto
    let tamanho: usize = numeros.len();

    // Opção 2: converta com `as`
    let tamanho_i32: i32 = numeros.len() as i32;

    // Opção 3: conversão segura com try_into
    use std::convert::TryInto;
    let tamanho_i32: i32 = numeros.len().try_into().unwrap();
}

Dica: Prefira try_into() sobre as quando há risco de overflow. O as trunca silenciosamente, enquanto try_into() retorna Result e permite tratamento de erro.

Solução 3: Desempacotar Option e Result

fn main() {
    let numeros = vec![10, 20, 30];

    // Opção 1: unwrap (panic se None)
    let primeiro: &i32 = numeros.get(0).unwrap();

    // Opção 2: valor padrão
    let primeiro: &i32 = numeros.get(0).unwrap_or(&0);

    // Opção 3: if let
    if let Some(primeiro) = numeros.get(0) {
        println!("Primeiro: {}", primeiro);
    }

    // Opção 4: match
    match numeros.get(0) {
        Some(valor) => println!("Valor: {}", valor),
        None => println!("Lista vazia"),
    }
}

Solução 4: Garantir Retorno Correto em Funções

// ERRO: a função retorna () no branch else
fn verificar(n: i32) -> bool {
    if n > 0 {
        true
    }
    // Falta o `else` — retorno implícito é ()
}

// CORRETO:
fn verificar(n: i32) -> bool {
    n > 0  // Expressão que retorna bool
}

Tabela de Conversões Comuns

DeParaMétodo
&strString.to_string(), String::from()
String&str&s, .as_str()
i32i64.into(), as i64
i64i32.try_into(), as i32
usizei32.try_into(), as i32
&[T]Vec<T>.to_vec()
Vec<T>&[T]&v, .as_slice()
Option<T>T.unwrap(), .unwrap_or(), ?
Result<T,E>T.unwrap(), ?, match

Veja Também