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
f64paraf32 - Overflow ao converter
i64parai32 - Problemas de memória ao confundir
String(owned) com&str(referência)
Os casos mais comuns deste erro são:
Stringvs&str— O par mais confuso para iniciantesi32vsusize— Retorno de.len()éusizeOption<T>vsT— Esqueceu de desempacotar oOptionResult<T, E>vsT— Esqueceu de tratar oResult()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
| De | Para | Método |
|---|---|---|
&str | String | .to_string(), String::from() |
String | &str | &s, .as_str() |
i32 | i64 | .into(), as i64 |
i64 | i32 | .try_into(), as i32 |
usize | i32 | .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 |