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.)
A mensagem de erro
Seção intitulada “A mensagem de erro”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"), }; println!("{}", user.name); println!("{}", user.emial); // ❌ erro de digitação: o campo é `email`}O que significa
Seção intitulada “O que significa”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 queusertem.
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.
Por que o Rust faz isso
Seção intitulada “Por que o Rust faz isso”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.
Como corrigir
Seção intitulada “Como corrigir”Correção 1: Corrija a grafia
Seção intitulada “Correção 1: Corrija a grafia”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"), }; println!("{}", user.name); println!("{}", user.email); // ✅ o nome certo do campo}Saída:
FerrisSe 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: FerrisNenhum usuário com id 2Primeiro usuário: AdaOs 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.
Correção 3: Use .0 e .1 em tuple structs
Seção intitulada “Correção 3: Use .0 e .1 em tuple structs”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 = 4distância ao quadrado = 251920x1080Tuplas 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!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.
Onde você costuma ver esse erro
Seção intitulada “Onde você costuma ver esse erro”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.
Erros relacionados
Seção intitulada “Erros relacionados”- E0599: no method named x found: a versão deste erro para métodos. As causas costumam ser as mesmas: erro de digitação ou um
Optionque precisa ser aberto. - E0425: cannot find value in this scope: a variável antes do ponto nem existe, ou você esqueceu o
self.antes de um campo. - E0308: mismatched types: o que costuma aparecer em seguida se você abre um
Optionnum lugar e não em outro. E0615: attempted to take value of method: você escreveux.lenem vez dex.len().E0616: field is private: o campo existe, mas não épub, como olende umVec. Use os métodos do tipo.E0560: struct has no field named x: o mesmo erro de digitação, só que ao criar a struct (User { emial: ... }).E0610: primitive type doesn't have fields:.alguma_coisanum número,boolouchar.- Todos os erros do compilador explicados
Teste o que aprendeu
Seção intitulada “Teste o que aprendeu”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
Para saber mais
Seção intitulada “Para saber mais”- E0609 — Rust error codes index (em inglês): a explicação oficial deste erro.
- Defining and Instantiating Structs — The Rust Book (em inglês): campos nomeados, acesso com ponto e a sintaxe de atualização de struct.
- Tuple Structs — The Rust Book (em inglês): structs cujos campos são acessados pela posição, como
.0. - The Option Enum — The Rust Book (em inglês): por que
Option<T>eTsão tipos diferentes e por que você precisa abrir oOption. - Traits: Defining Shared Behavior — The Rust Book (em inglês): métodos de trait e trait bounds, a ferramenta certa para código genérico.