Salta ai contenuti

Errore Rust E0599: no method named X found (metodo non trovato)

L’errore E0599 significa che hai chiamato un metodo che Rust non trova su quel tipo. È molto comune, e la causa di solito è una di poche: un trait che non hai importato, un Option che non hai aperto, un Vec trattato come se fosse un iteratore, oppure un semplice errore di battitura. La buona notizia è che le righe help: di rustc spesso ti dicono la soluzione esatta. Questa pagina fa parte della guida agli errori del compilatore.

error[E0599]: no method named `write_all` found for struct `Stdout` in the current scope
--> src/main.rs:5:9
|
5 | out.write_all(b"Server started\n").unwrap();
| ^^^^^^^^^
|
= note: the method is available for `Stdout` here
|
= help: items from traits can only be used if the trait is in scope
help: trait `Write` which provides `write_all` is implemented but not in scope; perhaps you want to import it
|
1 + use std::io::Write;
|

Il compilatore dice: «nessun metodo write_all trovato per la struct Stdout in questo scope», e suggerisce di importare il trait Write. Questo è il programma che l’ha prodotto:

use std::io;
fn main() {
let mut out = io::stdout();
out.write_all(b"Server started\n").unwrap();
}

I ^^^^ indicano il nome del metodo, write_all, e la prima riga ti dice su quale tipo Rust l’ha cercato: Stdout. Quindi il messaggio si legge così: «Ho guardato in Stdout e non ho trovato nessun metodo write_all che io possa usare qui».

Il tipo nella prima riga è l’indizio più utile di tutto il messaggio. Se dice Option<T>, Result<T, E>, Vec<_> o un parametro di tipo T quando ti aspettavi altro, sai già quale delle soluzioni qui sotto ti serve.

In Rust molti metodi non appartengono al tipo stesso. Arrivano da un trait. write_all viene dal trait Write, e Stdout lo implementa. Ma i metodi di un trait funzionano solo in un file che ha quel trait nello scope (ambito), di solito grazie a una riga use. Ecco cosa vuol dire «items from traits can only be used if the trait is in scope» (gli elementi dei trait si possono usare solo se il trait è nello scope).

Questa regola rende il codice prevedibile. Due crate diversi possono aggiungere entrambi un metodo write_all allo stesso tipo, tramite trait diversi. Se tutti i trait fossero sempre attivi, non potresti capire quale dei due intende una chiamata. Con la regola contano solo i trait che hai importato.

Le altre cause nascono dai tipi rigorosi di Rust. Un Option<&str> non è un &str, quindi non ha i metodi di &str. Un generico T potrebbe essere qualsiasi tipo, quindi Rust ti lascia chiamare solo i metodi promessi dai vincoli su T.

Scegli questa quando l’help dice «items from traits can only be used if the trait is in scope». Aggiungi la riga use suggerita da rustc.

use std::io::{self, Write};
fn main() {
let mut out = io::stdout();
out.write_all(b"Server avviato\n").unwrap();
}

Output:

Server avviato
▶ Provalo tu

Lo stesso succede con il crate rand. Senza l’import, rng.random_range(1..=6) non compila, e rustc ti dice quale trait portare nello scope:

help: trait `RngExt` which provides `random_range` is implemented but not in scope; perhaps you want to import it
|
1 + use rand::RngExt;

Attenzione: in rand 0.10 il trait si chiama RngExt. I tutorial più vecchi dicono use rand::Rng, e con rand 0.10 quell’import ti dà proprio questo stesso errore. Segui la riga help:, non il tutorial.

Soluzione 2: Estrai prima il valore dall’Option o dal Result

Sezione intitolata “Soluzione 2: Estrai prima il valore dall’Option o dal Result”

Scegli questa quando il tipo nell’errore è Option<T> o Result<T, E>. Il valore che ti serve sta dentro, e devi tirarlo fuori prima di chiamare i suoi metodi.

error[E0599]: no method named `to_uppercase` found for enum `Option<T>` in the current scope
--> src/main.rs:4:39
|
4 | println!("First guest: {}", first.to_uppercase());
| ^^^^^^^^^^^^ method not found in `Option<&&str>`

first() restituisce un Option, perché la lista potrebbe essere vuota. rustc suggerisce .expect(...), che funziona ma va in panic con None. Di solito è meglio gestire entrambi i casi:

fn main() {
let guests = vec!["ana", "bo", "chen"];
match guests.first() {
Some(name) => println!("Primo ospite: {}", name.to_uppercase()),
None => println!("Ancora nessun ospite"),
}
let shout = guests.last().map(|name| name.to_uppercase());
println!("Ultimo ospite: {:?}", shout);
}

Output:

Primo ospite: ANA
Ultimo ospite: Some("CHEN")
▶ Provalo tu

.map() esegue il tuo codice sul valore interno e lascia l’involucro Option. Per un Result (per esempio quello di .parse()), usa match, .unwrap_or(...) oppure ? dentro una funzione che restituisce Result. Vedi Option e Result.

Scegli questa quando chiami .map(), .filter() o un altro metodo degli iteratori direttamente su un Vec. Qui l’errore ha un aspetto diverso:

error[E0599]: the method `map` exists for struct `Vec<{integer}>`, but its trait bounds were not satisfied
--> src/main.rs:3:35
|
3 | let curved: Vec<i32> = scores.map(|s| s + 5).collect();
| ^^^
|
= note: the following trait bounds were not satisfied:
`Vec<{integer}>: Iterator`

Per alcuni metodi, come filter, il messaggio è più gentile: rustc dice che il Vec<i32> «is not an iterator» (non è un iteratore) e aggiunge help: call `.into_iter()` first. Stessa causa, stessa soluzione.

Vec<{integer}>: Iterator è «not satisfied» (non soddisfatto) perché un Vec è una collezione, non un iteratore. Chiedigliene uno con .iter() (prende in prestito gli elementi) o .into_iter() (li prende per sé):

fn main() {
let scores = vec![70, 85, 92];
let curved: Vec<i32> = scores.iter().map(|s| s + 5).collect();
println!("{:?}", curved);
}

Output:

[75, 90, 97]
▶ Provalo tu

Soluzione 4: Aggiungi un vincolo di trait al generico T

Sezione intitolata “Soluzione 4: Aggiungi un vincolo di trait al generico T”

Scegli questa quando l’errore dice che il metodo non è stato trovato «for type parameter T» (per il parametro di tipo T). Dentro una funzione generica, Rust sa solo quello che promettono i vincoli su T. Un T senza vincoli non promette niente, quindi non ha metodi.

error[E0599]: no method named `to_string` found for type parameter `T` in the current scope
--> src/main.rs:2:26
|
1 | fn describe<T>(item: T) -> String {
| - method `to_string` not found for this type parameter
2 | format!("[{}]", item.to_string())
| ^^^^^^^^^ method cannot be called on `T` due to unsatisfied trait bounds
|
help: the following trait defines an item `to_string`, perhaps you need to restrict type parameter `T` with it:
|
1 | fn describe<T: ToString>(item: T) -> String {
| ++++++++++

Aggiungi il vincolo che suggerisce rustc. Ora vengono accettati solo i tipi che si possono trasformare in stringa:

fn describe<T: ToString>(item: T) -> String {
format!("[{}]", item.to_string())
}
fn main() {
println!("{}", describe(42));
println!("{}", describe("pronto"));
}

Output:

[42]
[pronto]
▶ Provalo tu
  • Un errore di battitura o un metodo preso da un altro linguaggio. tasks.push_back(...) su un Vec dà help: you might have meant to use `push`, e tasks.lenght() dà help: there is a method `len` with a similar name. Soluzione: usa il nome suggerito da rustc.
  • Chiamare un Type::new() che non hai mai scritto. Rust non regala costruttori: Account::new("Ana", 100) fallisce con no associated function or constant named `new` found for struct `Account`. Soluzione: aggiungi impl Account { fn new(owner: &str, balance: i64) -> Self { ... } }, oppure costruiscilo direttamente con Account { owner: ..., balance: ... }. Vedi Struct.
  • Chiamare una funzione associata con il punto. Se fn new() -> Self non ha il parametro self, acc.new() fallisce con «this is an associated function, not a method» (è una funzione associata, non un metodo). Soluzione: chiamala sul tipo, Account::new().

Verifica veloce

1. rustc dice "items from traits can only be used if the trait is in scope". Cosa devi fare?

2. names.first().to_uppercase() fallisce con E0599 per Option<&&str>. Perché?

3. Chiami scores.filter(...) su un Vec<i32> e rustc dice "Vec<i32> is not an iterator". Come lo risolvi?

Punteggio: 0 / 3