Erro E0610 do Rust: primitive type doesn't have fields (campo num número)
O E0610 quer dizer: você escreveu alguma_coisa.campo, mas alguma_coisa é um número simples (ou um bool ou um char), e esses tipos não têm campo nenhum. Quase sempre significa que a variável não é o que você imagina. Talvez você já tenha tirado o número de dentro de uma struct, talvez uma função tenha devolvido um número em vez de uma struct, ou talvez você tenha escrito um .0 a mais. A mensagem mostra o tipo exato que o Rust está vendo, então você está a um passo da correção. (Esta página faz parte de Erros do compilador Rust explicados.)
A mensagem de erro
Seção intitulada “A mensagem de erro”error[E0610]: `{integer}` is a primitive type and therefore doesn't have fields --> src/main.rs:3:22 |3 | println!("{}", x.value); // ❌ x é um número, não tem campo `value` | ^^^^^Em português: “{integer} é um tipo primitivo e por isso não tem campos”.
Este é o programa que gera o erro:
fn main() { let x = 5; println!("{}", x.value); // ❌ x é um número, não tem campo `value`}O que significa
Seção intitulada “O que significa”O tipo entre crases é o que o Rust acha que a sua variável é. Aqui é `{integer}`, que significa “algum número inteiro, ainda não decidi de qual tipo” (o Rust escolhe i32 depois, se nada mais decidir). Você também vai ver `{float}` para números decimais, ou um tipo concreto como u32, usize, f64, bool ou char.
O ^^^^^ aponta para o nome depois do ponto. O Rust não está dizendo “value está escrito errado”. Ele está dizendo “um número não pode ter nenhum campo, seja qual for o nome”.
Então a pergunta a fazer é: por que essa variável é um número? É como pedir “a etiqueta de preço do 5”. Um produto tem etiqueta de preço. O número 5 é só um número.
Por que o Rust faz isso
Seção intitulada “Por que o Rust faz isso”Tipos primitivos como i32, u32, f64, bool e char são valores únicos, embutidos na linguagem. Eles não são structs, então não há nada dentro deles para alcançar com um ponto e um nome. Veja Tipos de dados.
Mas eles têm métodos: x.abs(), x.pow(2), price.round(), c.is_alphabetic(). Métodos são chamados com (). Campos são dados guardados dentro de uma struct, e só structs, tuplas e unions têm campos.
Como o Rust confere cada .campo em tempo de compilação, essa confusão nunca vira um crash ou um undefined silencioso em tempo de execução. Você descobre na hora, na linha exata.
Como corrigir
Seção intitulada “Como corrigir”Correção 1: Use o número diretamente
Seção intitulada “Correção 1: Use o número diretamente”Escolha esta quando você já tirou o campo de dentro da struct antes. Depois de let count = counter.value;, count é o número, então count.value pede um campo de um u32:
error[E0610]: `u32` is a primitive type and therefore doesn't have fields --> src/main.rs:8:26 |8 | println!("{}", count.value); | ^^^^^Ou você usa o número como ele é, ou volta para a struct e lê o campo de lá:
struct Counter { value: u32,}
fn main() { let mut counter = Counter { value: 0 }; counter.value += 3;
let count = counter.value; // agora count é um u32 simples println!("{}", count); // ✅ use o próprio número println!("{}", counter.value); // ✅ ou leia o campo da struct}Saída:
33Não sabe qual é o tipo de uma variável? Passe o mouse sobre ela no seu editor (com o rust-analyzer, veja Ferramentas do Rust). Ou escreva let _: () = count; por um momento: o rustc responde com expected `()`, found `usize` (esperava (), encontrou usize) e te diz o tipo real. Depois apague a linha.
Correção 2: Devolva ou guarde a struct, não o número
Seção intitulada “Correção 2: Devolva ou guarde a struct, não o número”Escolha esta quando o valor veio de uma função ou de uma coleção, e você esperava uma struct. Dois casos comuns:
- Uma função como
fn find_order() -> u32devolve o id de um pedido, e aíorder.totalfalha porqueorderé umu32. - Um
HashMap<&str, u32>guarda só as pontuações, e aíscores["ana"].scorefalha porque cada valor é umu32, não umPlayer.
Decida o que você quer de verdade. Se precisa de vários dados, troque o tipo de retorno ou o tipo do valor pela struct:
use std::collections::HashMap;
struct Order { id: u32, total: u32, // em centavos}
// devolve o Order inteiro, não só um númerofn new_order(id: u32, total: u32) -> Order { Order { id, total }}
struct Player { score: u32, level: u8,}
fn main() { let order = new_order(7, 1999); println!("O pedido {} custa {} centavos", order.id, order.total);
// o map guarda Players inteiros, não só as pontuações let mut players: HashMap<&str, Player> = HashMap::new(); players.insert("ana", Player { score: 10, level: 2 });
let ana = &players["ana"]; println!("ana: pontuação {}, nível {}", ana.score, ana.level);}Saída:
O pedido 7 custa 1999 centavosana: pontuação 10, nível 2Se um número é tudo de que você precisa, fique com o u32 e só tire a parte .campo. Veja Structs e HashMaps.
Correção 3: Tire o .0 a mais
Seção intitulada “Correção 3: Tire o .0 a mais”Escolha esta quando o erro aponta para um .0 ou .1. Tuplas e tuple structs usam posições como campos, e é fácil descer um nível a mais. Depois de let x = point.0;, x já é o número, então x.0 dá `{integer}` is a primitive type. O mesmo acontece com um tipo embrulho como struct Meters(u32);: um .0 tira o u32 de dentro, e um segundo .0 falha.
struct Meters(u32);
fn main() { let point = (3, 4); let x = point.0; // x já é o número 3 println!("x = {}", x); // não x.0
let m = Meters(42); println!("{} m", m.0); // um .0 tira o u32 de dentro da tuple struct
let line = ((0, 0), (3, 4)); // tuplas aninhadas: uma posição por nível println!("y final = {}", line.1.1);}Saída:
x = 342 my final = 4Conte os níveis: cada .0 ou .1 desce um nível dentro de uma tupla. Quando chegar a um número, pare. Se as posições ficarem confusas, dê nomes a elas com uma struct. Veja Acessando os elementos pela posição e Quando usar uma tupla e quando usar uma struct.
Onde você costuma ver esse erro
Seção intitulada “Onde você costuma ver esse erro”Um método sem (). price.round num f64, ou x.abs num i32, não é E0610, e sim E0615, “attempted to take value of method” (tentou pegar o valor de um método), porque esses nomes são métodos. Correção: price.round(), x.abs(). Uma surpresa: se o tipo ainda é {integer} (como em let x = -5;), até x.abs dá E0610, e x.abs() dá então o E0689 “can’t call method abs on ambiguous numeric type {integer}” (não dá para chamar o método abs num tipo numérico ambíguo). Escolha um tipo, let x: i32 = -5;, e x.abs() funciona.
Uma referência para um número. Se a variável é um &u32 em vez de um u32, o código muda para E0609: no field `value` on type `&u32`. Um lugar comum é um loop como for (name, score) in &players, onde score é um &i32, e score.1 dá no field `1` on type `&{integer}`. A causa e as correções são as mesmas: score já é o número.
O resultado de len() ou count(). let len = scores.len(); te dá um usize, então len.count falha com `usize` is a primitive type. Correção: use len diretamente.
Números com unidade. Se você quer price.cents ou distance.meters, crie uma struct pequena: struct Price { cents: u64 }. Aí price.cents funciona, e o tipo impede que você misture preços com outros números. Veja Structs.
Erros relacionados
Seção intitulada “Erros relacionados”- E0609: no field
xon typeT: o tipo não tem um campo com esse nome. É este que você recebe (e não o E0610) para referências como&u32. - E0615: attempted to take value of method: você escreveu
price.roundem vez deprice.round(). E0689: can't call method on ambiguous numeric type: um método num número cujo tipo ainda não foi escolhido. Acrescente um tipo comoi32.- E0599: no method named x found: a versão para métodos, por exemplo um método que números não têm.
- E0308: mismatched types: costuma aparecer junto com o E0610 quando uma função devolve um número, mas você esperava uma struct.
- Todos os erros do compilador explicados
Teste o que aprendeu
Seção intitulada “Teste o que aprendeu”Teste o que aprendeu
1. Depois de let total = cart.total; (sendo total um campo u32), o que total.amount dá?
2. O que {integer} significa na mensagem?
3. let x = point.0; e depois x.0 dá E0610. Qual é a correção?
Pontuação: 0 / 3
Para saber mais
Seção intitulada “Para saber mais”- E0610 — Rust error codes index (em inglês): a explicação oficial deste erro.
- Data Types — The Rust Book (em inglês): tipos escalares (inteiros, decimais,
bool,char) e tipos compostos (tuplas, arrays). - Tuple Structs — The Rust Book (em inglês): tipos embrulho como
Meters(u32)e como o.0funciona neles. - Primitives — Rust by Example (em inglês): os tipos embutidos, com exemplos para rodar.
i32— documentação da biblioteca padrão (em inglês): todos os métodos que um inteiro tem (e você vai ver que todos são chamados com()).