Pular para o conteúdo

Erro E0308 do Rust: mismatched types (tipos incompatíveis)

O E0308 quer dizer que o Rust esperava um valor de um tipo, mas você entregou um valor de outro tipo. É provavelmente o erro que você mais vai ver em Rust, e a mensagem quase sempre mostra os dois tipos, então a correção costuma ser rápida. Para os outros erros desta série, veja Erros do compilador Rust.

error[E0308]: mismatched types
--> src/main.rs:6:29
|
6 | let user = User { name: "Ferris" };
| ^^^^^^^^ expected `String`, found `&str`
|
help: try using a conversion method
|
6 | let user = User { name: "Ferris".to_string() };
| ++++++++++++

Em português: “tipos incompatíveis”. O compilador esperava uma String (expected String), encontrou um &str (found &str) e sugere um método de conversão, .to_string().

Este é o programa que gera o erro:

struct User {
name: String,
}
fn main() {
let user = User { name: "Ferris" };
println!("{}", user.name);
}

Toda mensagem do E0308 tem o mesmo formato: expected A, found B (esperava A, encontrou B).

  • Expected (esperado) é o tipo de que o Rust precisa naquele ponto. Ele vem de outro lugar: uma anotação de tipo, o parâmetro ou o retorno de uma função, um campo de struct ou outro ramo de um if/match.
  • Found (encontrado) é o tipo do valor que você realmente escreveu ali.

No exemplo, o campo name foi declarado como String, então é isso que o Rust espera. Mas "Ferris" é um literal de string, e o tipo dele é &str. Os dois são parentes, mas não são o mesmo tipo.

Olhe também as linhas help:. No E0308, o compilador muitas vezes sugere a correção exata, como o .to_string() acima.

O Rust nunca converte tipos escondido. Em algumas linguagens, um número vira texto sem avisar, ou um número grande é cortado sem aviso para caber num tipo menor. Essas conversões escondidas causam bugs de verdade: dados perdidos, resultados errados, falhas bem longe do erro original.

Por isso, o Rust deixa toda conversão visível no código. Dá um pouco mais de digitação, mas quando você reler o código depois, vai ver exatamente onde um valor muda de tipo e se essa etapa pode falhar. Os tipos em si estão em Tipos de dados.

Cada correção abaixo começa com o formato do erro, para você ir direto à que combina com o seu.

error[E0308]: mismatched types
--> src/main.rs:1:46
|
1 | fn total_price(price: u32, quantity: u32) -> u32 {
| ----------- ^^^ expected `u32`, found `()`
| |
| implicitly returns `()` as its body has no tail or `return` expression
2 | price * quantity;
| - help: remove this semicolon to return this value

Se aparecer found (), olhe a última linha da função. Em Rust, a última expressão de um bloco sem ponto e vírgula é o valor de retorno. Colocar ; transforma essa expressão numa instrução, e a função passa a retornar () (o tipo “nada”). O compilador diz isso com outras palavras: a função retorna () porque o corpo não tem expressão final nem return, e a ajuda pede para remover o ponto e vírgula. Apague o ;:

fn total_price(price: u32, quantity: u32) -> u32 {
price * quantity // sem ponto e vírgula: este valor é retornado
}
fn main() {
println!("Total: {}", total_price(250, 3));
}

Saída:

Total: 750
▶ Experimente você mesmo

A mesma coisa acontece dentro de blocos if e braços de match. Mais em Funções.

String é uma string com dono, que pode crescer. &str é uma visão emprestada de um texto. A história completa está em Strings. Converter é curto nos dois sentidos:

  • De &str para String: .to_string() ou String::from(...).
  • De String para &str: empreste com & (ou chame .as_str()).

O segundo caso aparece como expected &str, found String, normalmente quando você passa uma String para uma função que recebe &str.

struct User {
name: String,
}
fn greet(name: &str) {
println!("Olá, {name}!");
}
fn main() {
// &str -> String: .to_string() ou String::from()
let user = User { name: "Ferris".to_string() };
// String -> &str: empreste com & (ou chame .as_str())
greet(&user.name);
greet(user.name.as_str());
let mut tags: Vec<String> = Vec::new();
tags.push(String::from("rust"));
println!("{tags:?}");
}

Saída:

Olá, Ferris!
Olá, Ferris!
["rust"]
▶ Experimente você mesmo
error[E0308]: mismatched types
--> src/main.rs:3:22
|
3 | let count: i32 = names.len();
| --- ^^^^^^^^^^^ expected `i32`, found `usize`
| |
| expected due to this

Aqui o compilador esperava um i32 por causa da anotação (expected due to this) e encontrou um usize. i32, i64, u32 e usize são todos tipos diferentes, e o Rust não mistura nenhum deles. Tamanhos e índices são sempre usize. Escolha uma conversão:

  • .into() (ou i64::from(x)) quando a conversão nunca pode perder dados, como de i32 para i64.
  • i64::try_from(x) (ou .try_into()) quando o valor talvez não caiba. Ele retorna um Result, então você decide o que acontece se falhar.
  • as é a conversão rápida. Ela nunca falha, mas corta sem avisar os valores que não cabem, então use só quando você sabe que o valor é pequeno.
fn average(total: i64, count: i64) -> i64 {
total / count
}
fn main() {
let scores: Vec<i32> = vec![80, 95, 70];
let total: i32 = scores.iter().sum();
// i32 -> i64 sempre cabe, então .into() basta
let total: i64 = total.into();
// usize -> i64 talvez não caiba, então try_from confere antes
let count = i64::try_from(scores.len()).unwrap();
println!("Média: {}", average(total, count));
// `as` também funciona, mas corta sem avisar os valores que não cabem
let players = scores.len() as i32;
println!("{players} jogadores");
}

Saída:

Média: 81
3 jogadores
▶ Experimente você mesmo

Muitas vezes, a correção mais simples é trocar a anotação: let count: usize = names.len();. As páginas Conversão de tipos e Conversões vão mais fundo.

Correção 4: Coloque dentro de Some ou tire o valor do Option

Seção intitulada “Correção 4: Coloque dentro de Some ou tire o valor do Option”
error[E0308]: mismatched types
--> src/main.rs:3:27
|
3 | let position: usize = names.iter().position(|n| *n == "Ben");
| ----- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `Option<usize>`
| |
| expected due to this

Option<usize> não é um usize. É “talvez um usize, talvez nada”. Essa incompatibilidade aparece nos dois sentidos:

  • Expected T, found Option<T>: você precisa tratar o caso “nada”, com match, if let ou unwrap_or(padrão).
  • Expected Option<T>, found T: sua função retorna Option, então embrulhe o valor: Some(10) em vez de 10.
fn find_discount(code: &str) -> Option<u32> {
if code == "SAVE10" {
return Some(10); // T -> Option<T>: embrulhe em Some
}
None
}
fn main() {
let names = vec!["Ana", "Ben", "Cy"];
// Option<T> -> T: trate o caso "não encontrado"...
match names.iter().position(|n| *n == "Ben") {
Some(i) => println!("Ben está na posição {i}"),
None => println!("Ben não está aqui"),
}
// ...ou escolha um valor padrão com unwrap_or
let discount: u32 = find_discount("HELLO").unwrap_or(0);
println!("Desconto: {discount}%");
}

Saída:

Ben está na posição 1
Desconto: 0%
▶ Experimente você mesmo

O compilador pode sugerir .expect("REASON"). Isso também funciona, mas derruba o programa quando o valor é None, então use só quando None realmente não pode acontecer. Veja Option.

Correção 5: Faça todos os ramos de if/else e match terem o mesmo tipo

Seção intitulada “Correção 5: Faça todos os ramos de if/else e match terem o mesmo tipo”
error[E0308]: `if` and `else` have incompatible types
--> src/main.rs:3:52
|
3 | let label = if stock > 0 { "in stock" } else { stock };
| ---------- ^^^^^ expected `&str`, found integer
| |
| expected because of this

Em português: “if e else têm tipos incompatíveis”. Quando um if ou um match produz um valor, o Rust precisa saber qual é o tipo único dele. Por isso, todos os ramos precisam produzir o mesmo tipo. O primeiro ramo define o que se espera, e o erro aponta para o ramo que discorda. No match, a mensagem diz “match arms have incompatible types” (os braços do match têm tipos incompatíveis). Faça o ramo diferente concordar com os outros:

fn status_text(code: u16) -> String {
match code {
200 => "OK".to_string(),
404 => "Not Found".to_string(),
_ => format!("Código desconhecido {code}"), // todos os braços são String
}
}
fn main() {
let stock = 0;
let label = if stock > 0 { "in stock" } else { "sold out" }; // os dois são &str
println!("{label}");
println!("{}", status_text(404));
println!("{}", status_text(418));
}

Saída:

sold out
Not Found
Código desconhecido 418
▶ Experimente você mesmo

Aqui um dos braços precisava de format!, que cria uma String, então os outros braços também viraram String. Deixamos "in stock" e "sold out" em inglês para combinar com a mensagem de erro acima. Veja If…Else e Match.

  • Lendo a entrada do usuário. let age: u32 = input.trim(); dá “expected u32, found &str”. Texto não é número. Correção: converta com parse: let age: u32 = input.trim().parse().unwrap(); (ou trate o erro, veja Result).
  • Colocando literais num Vec<String>. tags.push("rust") dá “expected String, found &str”. Correção: tags.push("rust".to_string()), ou use um Vec<&str> se todas as strings forem literais.
  • Contas com tipos numéricos misturados. total + n, com total sendo i64 e n sendo i32, dá E0308 (e muitas vezes um E0277 junto). Correção: converta um dos lados antes: total + i64::from(n).

Teste o que aprendeu

1. Uma função diz -> i32, mas o erro diz "expected i32, found ()". Qual é a causa mais provável?

2. Uma função recebe &str e você tem let name = String::from("Ana");. Como você passa o valor?

3. Qual conversão nunca perde dados sem avisar?

Pontuação: 0 / 3