Salta ai contenuti

Errore Rust E0615: attempted to take value of method (metodo senza parentesi)

E0615 significa: hai scritto value.something senza parentesi, ma something è un metodo, non un campo. In Rust un metodo viene eseguito solo quando lo chiami con (), anche se non riceve argomenti. È un errore molto comune se arrivi da C#, JavaScript o Python, dove .Count, .length o una proprietà sembrano normali campi. La soluzione è quasi sempre lunga due caratteri. (Questa pagina fa parte di Errori del compilatore Rust spiegati.)

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` is a method
| ^^^ method, not a field
|
help: use parentheses to call the method
|
3 | println!("{} has {} letters", name, name.len()); // ❌ `len` is a method
| ++

Il compilatore ti dice: «hai provato a prendere il valore del metodo len sul tipo String». L’etichetta dice «metodo, non campo», e l’help suggerisce di usare le parentesi per chiamarlo.

Ecco il programma che lo provoca:

fn main() {
let name = String::from("Ferris");
println!("{} has {} letters", name, name.len); // ❌ `len` is a method
}

(Il commento dice: len è un metodo.)

Leggi la prima riga partendo dalla fine: on type String è il tipo che Rust ha guardato, e method len è quello che ci ha trovato. Quindi Rust len l’ha trovato. Solo che non è un valore che puoi leggere come un campo: è un pezzo di codice che deve essere eseguito.

L’etichetta method, not a field e i ++ sotto () nell’help dicono proprio questo: aggiungi le parentesi e hai finito.

Pensa a un distributore automatico. Un campo è un’etichetta stampata sulla macchina («Distributore n. 12»): la leggi e basta. Un metodo è un pulsante («Mostra il resto»): non succede niente finché non lo premi. name.len indica il pulsante. name.len() lo preme.

Rust tiene campi e metodi rigorosamente separati. Un campo è un dato salvato dentro la struct. Un metodo è una funzione definita in un blocco impl che riceve self. Leggi un campo con value.field e chiami un metodo con value.method(). Vedi Struct.

Molti linguaggi rendono sfumato questo confine con le proprietà: list.Count in C# o text.length in JavaScript sembrano campi, ma dietro le quinte eseguono codice. Rust non ha proprietà, ed è una scelta voluta. Quando vedi (), sai che viene eseguito del codice che può richiedere tempo; quando non le vedi, sai che è una semplice lettura di un dato salvato. Niente è nascosto.

Una struct può persino avere un campo e un metodo con lo stesso nome, come un campo privato balance e un metodo pubblico balance(). È possibile solo perché Rust non indovina mai quale dei due intendevi: senza parentesi è il campo, con le parentesi è il metodo.

Scegli questa strada quasi sempre. Segui la riga help: e chiama il metodo. Funziona allo stesso modo per i tuoi metodi (come total su un carrello della spesa) e per quelli standard come len, is_empty, trim e count:

struct Item {
price: u32, // in centesimi
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!("{} ha {} lettere", name, name.len()); // ✅ chiamato
let cart = Cart {
items: vec![Item { price: 250, qty: 2 }, Item { price: 100, qty: 1 }],
};
if !cart.items.is_empty() {
println!("Totale: {} centesimi", cart.total()); // ✅ chiamato
}
}

Output:

Ferris ha 6 lettere
Totale: 600 centesimi
▶ Provalo tu

Se il metodo ha bisogno di argomenti, rustc ti mostra dove vanno con un segnaposto _. Per s.contains l’help dice s.contains(_): completa con quello che cerchi, per esempio s.contains("world"). Se non sai cosa vuole un metodo, il prossimo errore di rustc (E0061) ti dirà quanti argomenti riceve.

Soluzione 2: passa il metodo come funzione, non come value.method

Sezione intitolata “Soluzione 2: passa il metodo come funzione, non come value.method”

Scegli questa strada quando non volevi chiamare il metodo subito, ma passarlo a qualcos’altro, come .map() o sort_by_key. Anche scrivere .map(words[0].len) dà E0615. Hai due buone alternative: una breve closure che chiama il metodo, oppure il percorso completo del metodo, String::len, che funziona come il nome di una funzione:

fn main() {
let words = vec![String::from("crab"), String::from("ferris"), String::from("rust")];
// una closure che chiama il metodo su ogni elemento
let lengths: Vec<usize> = words.iter().map(|w| w.len()).collect();
println!("{:?}", lengths);
// il percorso del metodo funziona come il nome di una funzione
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);
}

Output:

[4, 6, 4]
[4, 6, 4]
["crab", "rust", "ferris"]
▶ Provalo tu

Le parole restano in inglese perché l’output dipende dalla loro lunghezza in byte. String::len funziona qui perché iter() ti dà elementi &String, e String::len riceve un &String. Quando i tipi non combaciano esattamente, la closure |w| w.len() è la scelta semplice che funziona sempre.

Soluzione 3: rendilo un campo, se è davvero un dato salvato

Sezione intitolata “Soluzione 3: rendilo un campo, se è davvero un dato salvato”

Scegli questa strada quando l’errore è in una tua struct e ti accorgi che il valore dovrebbe semplicemente essere salvato. È una domanda di progettazione: campo o metodo?

  • Rendilo un campo quando il valore viene dato una volta e non dipende da nient’altro: un id, un name, una data created_at.
  • Rendilo un metodo quando il valore viene calcolato da altri dati: un total dagli articoli, un’area da larghezza e altezza, una len dal contenuto.

Un campo total salvato diventerebbe sbagliato nel momento in cui qualcuno aggiunge un articolo e si dimentica di aggiornarlo. Un metodo total() viene calcolato da capo ogni volta, quindi non può mai essere sbagliato. Qui id è un campo e total è un metodo, e puoi vedere che il metodo resta corretto mentre l’ordine cambia:

mod shop {
pub struct Order {
pub id: u32, // salvato: impostato una volta, si legge con order.id
prices: Vec<u32>, // privato: cambia solo attraverso i metodi
}
impl Order {
pub fn new(id: u32) -> Self {
Order { id, prices: Vec::new() }
}
pub fn add(&mut self, price: u32) {
self.prices.push(price);
}
// calcolato: corrisponde sempre ai prezzi, non può diventare vecchio
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!("Ordine {}: {} articoli, {} centesimi", order.id, order.item_count(), order.total());
order.add(50);
println!("Ordine {}: {} articoli, {} centesimi", order.id, order.item_count(), order.total());
}

Output:

Ordine 17: 2 articoli, 350 centesimi
Ordine 17: 3 articoli, 400 centesimi
▶ Provalo tu

C’è una via di mezzo che vedrai in molte librerie: un campo privato più un metodo pubblico con lo stesso nome (un «getter»). Il resto del codice può leggere il valore con acc.balance(), ma non può cambiarlo direttamente. Scrivere acc.balance da fuori del modulo dà allora un errore leggermente diverso, E0616 «field balance of struct Account is private», con il suggerimento a method `balance` also exists, call it with parentheses (esiste anche un metodo balance, chiamalo con le parentesi). Stessa soluzione: aggiungi (). Vedi Moduli.

Abitudini da altri linguaggi. name.len, list.is_empty, text.trim, words.iter().count danno tutti E0615. In Rust sono tutti metodi, quindi vogliono tutti (). Soluzione: name.len(), list.is_empty() e così via. Vedi Stringhe.

len su un Vec. scores.len è l’unica eccezione al messaggio: ottieni E0616, «field len of struct Vec is private», perché Vec ha davvero un campo nascosto chiamato len. rustc aggiunge comunque help: a method `len` also exists, call it with parentheses. Soluzione: scores.len(). (scores.is_empty dà il normale E0615.) Vedi Vettori.

Un metodo che cambia qualcosa. Anche counter.increment; da solo su una riga dà E0615, anche se non volevi un valore. Senza () non viene eseguito niente. Soluzione: counter.increment();.

Il contrario: chiamare un campo come un metodo. Se name è un campo, user.name() dà E0599 con l’etichetta field, not a method e help: remove the arguments. Soluzione: user.name. E se il campo contiene una closure, come on_click: Box<dyn Fn() -> String>, rustc dice surround the field access with parentheses: scrivi (button.on_click)() per chiamarla.

Verifica veloce

1. Ottieni «attempted to take value of method is_empty on type String». Qual è la soluzione?

2. Quale di questi di solito dovrebbe essere un metodo invece di un campo salvato?

3. Vuoi la lunghezza di ogni parola: words.iter().map(???). Cosa funziona?

Punteggio: 0 / 3