Errore Rust E0424: expected value, found module `self` (manca self)
E0424 significa che hai scritto self dentro una funzione che non ha un parametro self, quindi non esiste nessun «self» a cui Rust possa riferirsi. È una svista molto comune, soprattutto se arrivi da Java, C# o JavaScript, dove this c’è sempre. La soluzione di solito è una parola: aggiungi &self all’elenco dei parametri della funzione. Questa pagina fa parte della guida agli errori del compilatore.
Il messaggio di errore
Sezione intitolata “Il messaggio di errore”error[E0424]: expected value, found module `self` --> src/main.rs:7:32 |6 | fn describe() { | -------- this function doesn't have a `self` parameter7 | println!("Player: {}", self.name); // ❌ which player? | ^^^^ `self` value is a keyword only available in methods with a `self` parameter |help: add a `self` receiver parameter to make the associated `fn` a method |6 | fn describe(&self) { | +++++
error[E0599]: no method named `describe` found for struct `Player` in the current scope --> src/main.rs:13:10 |13 | hero.describe(); | ^^^^^^^^ this is an associated function, not a methodIl compilatore dice: «mi aspettavo un valore, ho trovato il modulo self», e spiega che self come valore esiste solo nei metodi con un parametro self. Questo è il programma che lo produce:
struct Player { name: String,}
impl Player { fn describe() { println!("Player: {}", self.name); // ❌ which player? }}
fn main() { let hero = Player { name: String::from("Ferris") }; hero.describe();}Il secondo errore, E0599, è un effetto collaterale del primo. Risolvi E0424 e sparisce anche lui.
Cosa significa
Sezione intitolata “Cosa significa”Guarda le due etichette che rustc mette sul tuo codice:
- Riga 6:
fn describe()ha le parentesi vuote. rustc nota che «this function doesn’t have aselfparameter» (questa funzione non ha un parametroself). - Riga 7:
self.namechiede il nome «del giocatore su cui è stata chiamata». Ma adescribenon è stato dato nessun giocatore, quindi quiselfnon significa niente.
Una funzione dentro un blocco impl senza parametro self è una funzione associata. Appartiene al tipo (Player::describe()), non a un valore particolare. Una funzione con un parametro self è un metodo, e solo i metodi possono dire self.
Pensa a un modulo cartaceo che dice «scrivi qui il tuo nome». Ha senso solo quando qualcuno ha il foglio in mano. describe() è un modulo che non tiene in mano nessuno.
Perché dice «found module self»? In Rust self ha un secondo significato: il modulo corrente, come in use self::helpers;. Quando non c’è un valore self, rustc ripiega su quel significato e poi si lamenta che un modulo non è un valore. Puoi ignorare la parola «module»; il vero problema è il parametro mancante.
Perché Rust si comporta così
Sezione intitolata “Perché Rust si comporta così”In Rust niente è nascosto. Java e C# danno a ogni metodo non statico un this invisibile. Rust ti fa scrivere self come primo parametro, e dici anche come il metodo lo riceve: solo per guardarlo, per cambiarlo o per prenderne l’ownership (possesso). Quella scelta fa parte delle regole del prestito (borrowing), quindi deve essere visibile.
Così chiunque legga il tuo codice, compreso il compilatore, sa esattamente cosa fa un metodo al suo valore. fn summary(&self) promette di non cambiare niente. fn add(&mut self, ...) avvisa che cambia qualcosa. fn checkout(self) consuma il valore. Poi il compilatore controlla quelle promesse al posto tuo. I metodi e i blocchi impl sono spiegati in Struct.
Come risolverlo
Sezione intitolata “Come risolverlo”Soluzione 1: Aggiungi &self, &mut self o self
Sezione intitolata “Soluzione 1: Aggiungi &self, &mut self o self”Scegli questa quando la funzione deve lavorare su un valore che esiste già. Nella maggior parte dei casi è la soluzione giusta. Aggiungi il parametro suggerito da rustc e chiama la funzione con il punto:
struct Player { name: String,}
impl Player { fn describe(&self) { println!("Giocatore: {}", self.name); }}
fn main() { let hero = Player { name: String::from("Ferris") }; hero.describe();}Output:
Giocatore: Ferrisrustc suggerisce sempre &self, ma ne esistono tre tipi. Scegli quello che corrisponde a ciò che fa il metodo:
| Parametro | Il metodo… | Nomi tipici |
|---|---|---|
&self |
legge soltanto il valore | summary, len, is_empty, describe |
&mut self |
cambia il valore | add, push, reset, level_up |
self |
consuma il valore (ne prende l’ownership) | into_..., finish, build, checkout |
Eccoli tutti e tre sullo stesso tipo:
struct Order { items: Vec<String>,}
impl Order { fn summary(&self) -> String { format!("{} articoli: {}", self.items.len(), self.items.join(", ")) }
fn add(&mut self, item: &str) { self.items.push(item.to_string()); }
fn checkout(self) -> Vec<String> { self.items }}
fn main() { let mut order = Order { items: Vec::new() }; order.add("caffè"); order.add("cornetto"); println!("{}", order.summary()); let packed = order.checkout(); // qui order viene consumato println!("Impacchettato: {:?}", packed);}Output:
2 articoli: caffè, cornettoImpacchettato: ["caffè", "cornetto"]Se scegli &self ma il metodo cambia un campo, il compilatore te lo dice subito (E0594 «cannot assign to self.count, which is behind a & reference», oppure E0596 per chiamate come push). Passa a &mut self. Nel dubbio, parti da &self; self è il caso raro.
Soluzione 2: Chiamalo come Type::method(&value)
Sezione intitolata “Soluzione 2: Chiamalo come Type::method(&value)”Una volta che una funzione ha un parametro self, puoi chiamarla in due modi. hero.label() e Player::label(&hero) significano esattamente la stessa cosa: il valore che metteresti prima del punto diventa il primo argomento. La forma con il punto è quella che scriverai quasi sempre. La forma Type:: è comoda quando vuoi passare il metodo stesso a qualcosa come map:
struct Player { name: String, level: u32,}
impl Player { fn label(&self) -> String { format!("{} (livello {})", self.name, self.level) }}
fn main() { let hero = Player { name: String::from("Ferris"), level: 3 }; println!("{}", hero.label()); println!("{}", Player::label(&hero)); // la stessa chiamata
let team = vec![hero, Player { name: String::from("Corro"), level: 5 }]; let labels: Vec<String> = team.iter().map(Player::label).collect(); println!("{:?}", labels);}Output:
Ferris (livello 3)Ferris (livello 3)["Ferris (livello 3)", "Corro (livello 5)"]Quello che non funziona è Player::label() senza un valore: un metodo ha sempre bisogno del suo self. Se lo ometti, ottieni E0061, «this function takes 1 argument but 0 arguments were supplied».
Soluzione 3: Passa ciò che serve alla funzione
Sezione intitolata “Soluzione 3: Passa ciò che serve alla funzione”Scegli questa quando non c’è ancora un valore, o quando la funzione non sta proprio in un blocco impl.
Il caso classico è un costruttore. new crea il valore, quindi non c’è nessun self da cui leggere. Dai a new i dati iniziali come normali parametri e costruisci la struct con Self { ... }:
struct Counter { count: u32, step: u32,}
impl Counter { fn new(step: u32) -> Self { Self { count: 0, step } }
fn tick(&mut self) { self.count += self.step; }}
fn main() { let mut counter = Counter::new(5); counter.tick(); counter.tick(); println!("Conteggio: {}", counter.count);}Output:
Conteggio: 10Fai caso alle maiuscole: Self (con la S maiuscola) è il tipo, quindi Self { ... } costruisce un nuovo valore. self (con la s minuscola) è il valore su cui è stato chiamato un metodo.
La stessa idea risolve il caso di una funzione libera, fuori da qualsiasi blocco impl. Lì rustc lo dice in modo leggermente diverso: «this function can’t have a self parameter» (questa funzione non può avere un parametro self). Passa invece la struct per riferimento:
struct Cart { items: Vec<u32>,}
fn total(cart: &Cart) -> u32 { cart.items.iter().sum()}
// chiamala così: total(&cart)Oppure sposta total dentro impl Cart e dagli &self, come nella Soluzione 1. Vanno bene entrambe; un metodo si legge in modo più naturale quando la funzione riguarda chiaramente un carrello.
Dove lo incontri di solito
Sezione intitolata “Dove lo incontri di solito”Dentro new. Scrivere self.count = 0; in fn new() -> Self dà E0424, e il suggerimento di rustc fn new(&self) -> Self non avrebbe senso (ti servirebbe un contatore per creare un contatore). Soluzione: costruisci il valore con Self { count: 0 }, come nella Soluzione 3.
Dentro una closure in una funzione associata. Il problema non è la closure, ma la funzione che la contiene:
error[E0424]: expected value, found module `self` --> src/main.rs:8:35 |7 | fn sale_prices(prices: &[u32]) -> Vec<u32> { | ----------- this function doesn't have a `self` parameter8 | prices.iter().map(|p| p - self.discount).collect() | ^^^^ `self` value is a keyword only available in methods with a `self` parameterSoluzione: rendila un metodo, fn sale_prices(&self, prices: &[u32]), oppure passa il numero come parametro, fn sale_prices(prices: &[u32], discount: u32). Vedi Closure.
In main o in una funzione di supporto. self non funziona mai fuori da un blocco impl. Soluzione: passa la struct come parametro (total(&cart)), oppure chiama un metodo su di lei (cart.total()).
Se arrivi dai metodi «static» di C# o Java. Una funzione associata senza self è la versione Rust di un metodo statico. Se vuoi la versione di istanza, devi aggiungere tu il parametro self. Vedi Rust per sviluppatori C#.
Errori correlati
Sezione intitolata “Errori correlati”- E0425: la svista opposta. Sei in un metodo, ma hai scritto
countinvece diself.count. - E0599: «no method named … found». Spesso stampato subito dopo E0424, perché una funzione senza
selfnon si può chiamare con il punto. - E0061: numero sbagliato di argomenti, per esempio quando chiami
Player::label()senza il valore. - E0596: hai aggiunto
&self, ma il metodo cambia un campo, quindi gli serve&mut self. Assegnare un campo in questo modo dà E0594. - Tutti gli errori del compilatore: l’elenco completo delle pagine sugli errori.
Verifica veloce
Sezione intitolata “Verifica veloce”Verifica veloce
1. Dentro impl Player, fn heal() { self.hp += 10; } dà E0424. Qual è la soluzione migliore?
2. Perché fn new() -> Self non può usare self.name?
3. Su Player è definito fn label(&self) -> String. Quale chiamata equivale a hero.label()?
Punteggio: 0 / 3
Per approfondire
Sezione intitolata “Per approfondire”- E0424 — Rust error codes index (in inglese): la spiegazione ufficiale di questo errore, con un breve esempio.
- Method Syntax — The Rust Book (in inglese):
&self,&mut selfeself, più le funzioni associate comenew. - Associated functions and methods — Rust by Example (in inglese): esempi eseguibili dei due tipi, fianco a fianco.
- The
selfkeyword — standard library docs (in inglese): entrambi i significati diself, il ricevente del metodo e il modulo corrente. - Associated items — The Rust Reference (in inglese): le regole esatte per metodi, ricevitori e funzioni associate.