Pular para o conteúdo

Erro E0609 do Rust: no field X on type T (campo inexistente)

O E0609 quer dizer: você escreveu valor.alguma_coisa, mas o tipo de valor não tem nenhum campo chamado alguma_coisa. Na maioria das vezes é um erro de digitação, ou o valor não é bem do tipo que você imagina (um Option<User> em vez de um User, por exemplo). A mensagem de erro mostra o tipo exato que o Rust está vendo, e isso costuma bastar para corrigir. (Esta página faz parte de Erros do compilador Rust explicados.)

error[E0609]: no field `emial` on type `User`
--> src/main.rs:12:25
|
12 | println!("{}", user.emial); // ❌ erro de digitação: o campo é `email`
| ^^^^^ unknown field
|
help: a field with a similar name exists
|
12 - println!("{}", user.emial); // ❌ erro de digitação: o campo é `email`
12 + println!("{}", user.email); // ❌ erro de digitação: o campo é `email`
|

Em português: “não existe o campo emial no tipo User”. O rustc marca o nome desconhecido e avisa que existe um campo com nome parecido, email.

Este é o programa que gera o erro:

struct User {
name: String,
email: String,
}
fn main() {
let user = User {
name: String::from("Ferris"),
email: String::from("[email protected]"),
};
println!("{}", user.name);
println!("{}", user.emial); // ❌ erro de digitação: o campo é `email`
}

O título da mensagem tem duas partes, e as duas importam:

  • no field `emial`: o nome depois do ponto que o Rust não encontrou.
  • on type `User`: o tipo que o Rust acha que user tem.

Então, primeiro confira o nome, depois confira o tipo. Se o tipo na mensagem não for a struct que você esperava, por exemplo Option<User>, &Option<User>, Vec<User> ou &T, o nome está certo: o problema é que você precisa tirar a struct de dentro primeiro.

É como pedir numa loja “o azul da segunda prateleira” estando na loja errada. O pedido está certo, você só está no lugar errado.

Em Rust, os campos de toda struct ficam definidos quando você escreve a struct. O compilador conhece a lista completa e confere cada .campo em tempo de compilação. Não existe “procurar em tempo de execução e receber undefined”, como no JavaScript, nem AttributeError, como no Python: um nome de campo errado nunca chega ao seu programa. Veja Structs.

Tipos que embrulham outros, como Option<T> e Result<T, E>, são tipos à parte: a sua struct fica dentro deles, mas eles não têm os campos dela. O Rust não atravessa o embrulho por você, porque o valor pode nem estar lá. Você precisa dizer o que acontece quando ele falta. Veja Option.

Código genérico segue a mesma regra rígida. Em fn greet<T>(x: &T), T pode ser qualquer tipo, inclusive i32 ou bool, que não têm campos. O Rust confere uma função genérica uma vez só, valendo para todo T possível, então ele só deixa você usar o que os seus trait bounds (restrições de trait) prometem. E um trait só pode prometer métodos, não campos. Veja Genéricos e Traits.

Escolha esta quando o tipo na mensagem é a struct que você esperava. Procure a linha help: a field with a similar name exists (existe um campo com nome parecido); o rustc geralmente sabe exatamente qual campo você quis dizer:

struct User {
name: String,
email: String,
}
fn main() {
let user = User {
name: String::from("Ferris"),
email: String::from("[email protected]"),
};
println!("{}", user.name);
println!("{}", user.email); // ✅ o nome certo do campo
}

Saída:

▶ Experimente você mesmo

Se não houver dica, vá até a definição da struct (na maioria dos editores: Ctrl+clique ou F12 no tipo) e copie o nome de lá. Nomes de campos diferenciam maiúsculas de minúsculas, e userName e user_name são campos diferentes.

Correção 2: Tire a struct de dentro do Option primeiro

Seção intitulada “Correção 2: Tire a struct de dentro do Option primeiro”

Escolha esta quando a mensagem diz que o tipo é Option<User>, Option<&User> ou &Option<User>. Isso acontece o tempo todo com funções que podem não encontrar algo, e com métodos como .first(), .last(), .get() e .find():

error[E0609]: no field `name` on type `Option<User>`
--> src/main.rs:11:25
|
11 | println!("{}", user.name);
| ^^^^ unknown field
|
help: one of the expressions' fields has a field of the same name
|
11 | println!("{}", user.unwrap().name);
| +++++++++

O rustc percebe que o valor dentro do Option tem um campo name e sugere .unwrap(). Essa dica compila, mas derruba o programa quando o valor é None. Melhor: decida o que acontece nos dois casos, com if let, match ou map mais um valor padrão:

struct User {
name: String,
}
fn find_user(id: u32) -> Option<User> {
if id == 1 {
Some(User { name: String::from("Ferris") })
} else {
None
}
}
fn main() {
// if let: só roda o código quando existe um User
if let Some(user) = find_user(1) {
println!("Encontrado: {}", user.name);
}
// match: trata os dois casos
match find_user(2) {
Some(user) => println!("Encontrado: {}", user.name),
None => println!("Nenhum usuário com id 2"),
}
// map + unwrap_or: pega um campo ou usa um valor padrão
let users = vec![User { name: String::from("Ada") }];
let first_name = users.first().map(|u| u.name.as_str()).unwrap_or("ninguém");
println!("Primeiro usuário: {first_name}");
}

Saída:

Encontrado: Ferris
Nenhum usuário com id 2
Primeiro usuário: Ada
▶ Experimente você mesmo

Os mesmos padrões funcionam com uma referência para um Option. Se maybe_user é um &Option<User>, escreva if let Some(user) = maybe_user e user será um &User. (Para &Option<User> o rustc não dá dica nenhuma, então vale a pena lembrar disso.) Dentro de uma função que devolve Option, let user = find_user(1)?; também funciona. Veja Tratando um Option com match e if let.

Escolha esta quando o tipo é uma tuple struct (struct de tupla), como struct Point(i32, i32);. Os campos dela não têm nome, só posição, então p.x dá no field `x` on type `Point` (com a dica p.0). Use a posição ou desmonte o valor em variáveis com nome. Se você quer nomes, transforme numa struct com campos nomeados:

struct Point(i32, i32); // tuple struct: os campos são .0 e .1
struct Size {
width: u32, // campos nomeados: use .width e .height
height: u32,
}
fn main() {
let p = Point(3, 4);
println!("x = {}, y = {}", p.0, p.1);
let Point(x, y) = p; // ou desmonte em variáveis com nome
println!("distância ao quadrado = {}", x * x + y * y);
let screen = Size { width: 1920, height: 1080 };
println!("{}x{}", screen.width, screen.height);
}

Saída:

x = 3, y = 4
distância ao quadrado = 25
1920x1080
▶ Experimente você mesmo

Tuplas comuns funcionam do mesmo jeito. pair.2 numa tupla de dois elementos dá E0609 com a nota available fields are: `0`, `1` (os campos disponíveis são 0 e 1). Veja Acessando os elementos pela posição.

Correção 4: Use um método de trait em vez de um campo num T genérico

Seção intitulada “Correção 4: Use um método de trait em vez de um campo num T genérico”

Escolha esta quando a mensagem diz on type `&T` (ou T), com o rótulo type parameter 'T' declared here (parâmetro de tipo ‘T’ declarado aqui). Um código assim falha, mesmo que todo tipo que você passar tenha um campo name:

fn greet<T>(animal: &T) {
println!("Olá, {}!", animal.name); // ❌ no field `name` on type `&T`
}

Traits não podem exigir campos, mas podem exigir um método getter (de leitura). Defina um trait pequeno, implemente para cada tipo e coloque-o como bound em T:

trait Named {
fn name(&self) -> &str;
}
struct Dog {
name: String,
}
struct Robot {
model: String,
}
impl Named for Dog {
fn name(&self) -> &str {
&self.name
}
}
impl Named for Robot {
fn name(&self) -> &str {
&self.model // o campo nem precisa ter o mesmo nome
}
}
fn greet<T: Named>(thing: &T) {
println!("Olá, {}!", thing.name()); // uma chamada de método, não um campo
}
fn main() {
greet(&Dog { name: String::from("Rex") });
greet(&Robot { model: String::from("R2-D2") });
}

Saída:

Olá, Rex!
Olá, R2-D2!
▶ Experimente você mesmo

Se a função só trabalha com um tipo, você nem precisa de genérico: escreva fn greet(animal: &Dog) e animal.name funciona. Veja Traits como parâmetros de função.

Um método sem (). Escrever word.len ou cart.total (sendo total um método) é parecido com o E0609, mas o rustc dá outro código: E0615, “attempted to take value of method” (tentou pegar o valor de um método), com a dica “use parentheses to call the method” (use parênteses para chamar o método). Num Vec, scores.len dá E0616, “field len of struct Vec is private” (o campo len da struct Vec é privado), porque o Vec tem mesmo um campo escondido com esse nome. A correção é a mesma nos dois casos: word.len(), scores.len(), cart.total(). E "hello".length é um E0609 comum em &str: em Rust, o nome é len(). Veja Strings.

Um Vec em vez de um item só. dogs.name num Vec<Dog> dá no field `name` on type `Vec<Dog>`. Uma lista não tem name; cada item tem. Correção: escolha um com dogs[0].name ou percorra com for dog in &dogs { println!("{}", dog.name); }. Veja Vetores.

Um Result em vez do valor. load_config().port dá no field `port` on type `Result<Config, String>`, com a mesma dica de .unwrap(). Correção: let config = load_config()?; dentro de uma função que devolve Result, ou um match. Veja O operador ?.

Um campo num número. age.value num u32 dá outro código ainda: E0610, “u32 is a primitive type and therefore doesn’t have fields” (u32 é um tipo primitivo e por isso não tem campos). Números, bool e char não têm campo nenhum; você provavelmente queria uma variável de struct.

Teste o que aprendeu

1. O erro diz que não existe o campo name no tipo Option<User>. O que isso indica?

2. Como você lê o primeiro campo de struct Meters(f64); num valor m?

3. Em fn show<T>(x: &T), como você pode ler um nome de qualquer T?

Pontuação: 0 / 3