Pular para o conteúdo

Erro E0615 do Rust: attempted to take value of method (método sem parênteses)

O E0615 quer dizer: você escreveu valor.alguma_coisa sem parênteses, mas alguma_coisa é um método, não um campo. Em Rust, um método só roda quando você o chama com (), mesmo que ele não receba nenhum argumento. Esse erro é muito comum para quem vem de C#, JavaScript ou Python, onde .Count, .length ou uma property parecem campos normais. A correção quase sempre tem dois caracteres. (Esta página faz parte de Erros do compilador Rust explicados.)

error[E0615]: attempted to take value of method `len` on type `String`
--> src/main.rs:3:46
|
3 | println!("{} has {} letters", name, name.len); // ❌ `len` é um método
| ^^^ method, not a field
|
help: use parentheses to call the method
|
3 | println!("{} has {} letters", name, name.len()); // ❌ `len` é um método
| ++

Em português: “tentou pegar o valor do método len no tipo String”. O rótulo diz “método, não um campo”, e a ajuda diz “use parênteses para chamar o método”.

Este é o programa que gera o erro:

fn main() {
let name = String::from("Ferris");
println!("{} has {} letters", name, name.len); // ❌ `len` é um método
}

Leia a primeira linha de trás para frente: on type String é o tipo que o Rust olhou, e method len é o que ele encontrou ali. Ou seja, o Rust achou o len. Só que ele não é um valor que você lê como um campo. É um pedaço de código que precisa rodar.

O rótulo method, not a field e o ++ embaixo do () na ajuda dizem exatamente isso: coloque os parênteses e pronto.

Pense numa máquina de vender salgadinhos. Um campo é uma etiqueta impressa na máquina (“Máquina nº 12”): você só lê. Um método é um botão (“Mostrar meu troco”): nada acontece até você apertar. name.len aponta para o botão. name.len() aperta o botão.

O Rust separa campos e métodos com rigor. Um campo é um dado guardado dentro da struct. Um método é uma função definida num bloco impl que recebe self. Você lê um campo com valor.campo e chama um método com valor.metodo(). Veja Adicionando comportamento com métodos.

Muitas linguagens misturam as duas coisas com properties (propriedades): list.Count em C# ou text.length em JavaScript parecem campos, mas rodam código por trás. O Rust não tem properties, e isso é de propósito. Quando você vê (), sabe que código vai rodar e pode levar tempo; quando não vê, sabe que é só a leitura de um dado guardado. Nada fica escondido.

Uma struct pode até ter um campo e um método com o mesmo nome, como um campo privado balance e um método público balance(). Isso só é possível porque o Rust nunca adivinha qual dos dois você quis: sem parênteses é o campo, com parênteses é o método.

Escolha esta quase sempre. Siga a linha help: e chame o método. Funciona igual para os seus próprios métodos (como total num carrinho de compras) e para os da biblioteca padrão, como len, is_empty, trim e count:

struct Item {
price: u32, // em centavos
qty: u32,
}
struct Cart {
items: Vec<Item>,
}
impl Cart {
fn total(&self) -> u32 {
self.items.iter().map(|i| i.price * i.qty).sum()
}
}
fn main() {
let name = String::from("Ferris");
println!("{} tem {} letras", name, name.len()); // ✅ chamado
let cart = Cart {
items: vec![Item { price: 250, qty: 2 }, Item { price: 100, qty: 1 }],
};
if !cart.items.is_empty() {
println!("Total: {} centavos", cart.total()); // ✅ chamado
}
}

Saída:

Ferris tem 6 letras
Total: 600 centavos
▶ Experimente você mesmo

Se o método precisar de argumentos, o rustc mostra onde eles entram com um marcador _. Para s.contains, a ajuda mostra s.contains(_): preencha com o que você está procurando, como s.contains("world"). Se não tiver certeza do que o método precisa, o próximo erro do rustc (E0061) vai dizer quantos argumentos ele recebe.

Correção 2: Passe o método como função, não como valor.metodo

Seção intitulada “Correção 2: Passe o método como função, não como valor.metodo”

Escolha esta quando você não queria chamar o método agora, e sim entregá-lo para outra coisa, como .map() ou sort_by_key. Escrever .map(words[0].len) também dá E0615. Você tem duas boas opções: uma closure curta que chama o método, ou o caminho completo do método, String::len, que funciona como o nome de uma função:

fn main() {
let words = vec![String::from("crab"), String::from("ferris"), String::from("rust")];
// uma closure que chama o método em cada item
let lengths: Vec<usize> = words.iter().map(|w| w.len()).collect();
println!("{:?}", lengths);
// o caminho do método funciona como o nome de uma função
let same: Vec<usize> = words.iter().map(String::len).collect();
println!("{:?}", same);
let mut sorted = words.clone();
sorted.sort_by_key(|w| w.len());
println!("{:?}", sorted);
}

Saída:

[4, 6, 4]
[4, 6, 4]
["crab", "rust", "ferris"]
▶ Experimente você mesmo

String::len funciona aqui porque iter() te dá itens &String, e String::len recebe um &String. Quando os tipos não batem exatamente, a closure |w| w.len() é a escolha fácil que sempre funciona.

Correção 3: Transforme em campo, se for mesmo um dado guardado

Seção intitulada “Correção 3: Transforme em campo, se for mesmo um dado guardado”

Escolha esta quando o erro está na sua struct e você percebe que o valor deveria simplesmente ser guardado. É uma questão de design: campo ou método?

  • Faça um campo quando o valor é definido uma vez e não depende de mais nada: um id, um name, uma data created_at.
  • Faça um método quando o valor é calculado a partir de outros dados: um total a partir dos itens, uma area a partir da largura e da altura, um len a partir do conteúdo.

Um campo total guardado ficaria desatualizado no momento em que alguém acrescentasse um item e esquecesse de atualizá-lo. Um método total() é calculado de novo toda vez, então nunca fica errado. Aqui id é um campo e total é um método, e dá para ver que o método continua certo enquanto o pedido muda:

mod shop {
pub struct Order {
pub id: u32, // guardado: definido uma vez, lido com order.id
prices: Vec<u32>, // privado: só muda pelos métodos
}
impl Order {
pub fn new(id: u32) -> Self {
Order { id, prices: Vec::new() }
}
pub fn add(&mut self, price: u32) {
self.prices.push(price);
}
// calculado: sempre bate com os preços, nunca fica desatualizado
pub fn total(&self) -> u32 {
self.prices.iter().sum()
}
pub fn item_count(&self) -> usize {
self.prices.len()
}
}
}
use shop::Order;
fn main() {
let mut order = Order::new(17);
order.add(250);
order.add(100);
println!("Pedido {}: {} itens, {} centavos", order.id, order.item_count(), order.total());
order.add(50);
println!("Pedido {}: {} itens, {} centavos", order.id, order.item_count(), order.total());
}

Saída:

Pedido 17: 2 itens, 350 centavos
Pedido 17: 3 itens, 400 centavos
▶ Experimente você mesmo

Existe um meio-termo que você vai ver em muitas bibliotecas: um campo privado mais um método público com o mesmo nome (um “getter”). O resto do código consegue ler o valor com acc.balance(), mas não consegue alterá-lo diretamente. Escrever acc.balance fora do módulo dá então um erro um pouco diferente, o E0616 “field balance of struct Account is private” (o campo balance da struct Account é privado), com a dica a method `balance` also exists, call it with parentheses (também existe um método balance, chame-o com parênteses). A correção é a mesma: coloque (). Veja Tudo é privado por padrão.

Hábitos de outras linguagens. name.len, list.is_empty, text.trim e words.iter().count dão E0615. Todos são métodos em Rust, então todos precisam de (). Correção: name.len(), list.is_empty() e assim por diante. Veja Métodos de string úteis.

len num Vec. scores.len é a única exceção à mensagem: você recebe o E0616, “field len of struct Vec is private”, porque o Vec tem mesmo um campo escondido chamado len. O rustc ainda acrescenta help: a method `len` also exists, call it with parentheses. Correção: scores.len(). (scores.is_empty dá o E0615 normal.) Veja Vetores.

Um método que altera alguma coisa. counter.increment; sozinho numa linha também dá E0615, mesmo que você não quisesse um valor. Sem (), nada roda. Correção: counter.increment();.

O contrário: chamar um campo como método. Se name é um campo, user.name() dá E0599 com o rótulo field, not a method (campo, não um método) e help: remove the arguments (remova os argumentos). Correção: user.name. E se o campo guarda uma closure, como on_click: Box<dyn Fn() -> String>, o rustc diz surround the field access with parentheses (coloque o acesso ao campo entre parênteses): escreva (button.on_click)() para chamá-la.

Teste o que aprendeu

1. Aparece "attempted to take value of method is_empty on type String". Qual é a correção?

2. Qual destes normalmente deveria ser um método em vez de um campo guardado?

3. Você quer o tamanho de cada palavra: words.iter().map(???). O que funciona?

Pontuação: 0 / 3