Storia della questione
Rust prende in prestito il concetto di metodi dai linguaggi orientati agli oggetti, ma li realizza in modo diverso: invece del consueto this o self, i metodi accettano esplicitamente un parametro self. Le funzioni associate sono nate come alternativa ai metodi statici in altri linguaggi di programmazione — sono legate a un tipo, ma non a un valore specifico.
Problema
Spesso si confondono metodi, funzioni associate e funzioni libere. Non è chiaro quando utilizzare un metodo con self o una funzione associata senza self. Ci sono questioni di visibilità, autodereferenza e trasferimento di proprietà.
Soluzione
I metodi in Rust si dichiarano con il primo parametro self/ &self/ &mut self all'interno di un blocco impl (di solito per struct o enum). Vengono invocati su un'istanza: oggetto.metodo(). Le funzioni associate (ad esempio, new, from) vengono dichiarate anch'esse all'interno di impl, ma senza il primo parametro self e vengono invocate tramite due punti: Tipo::funzione().
Esempio di codice:
struct Point { x: f64, y: f64, } impl Point { // Funzione associata (costruttore) fn new(x: f64, y: f64) -> Self { Self { x, y } } // Metodo: richiede self fn distance_from_origin(&self) -> f64 { (self.x.powi(2) + self.y.powi(2)).sqrt() } } let p = Point::new(3.0, 4.0); printf!("{}", p.distance_from_origin()); // 5.0
Caratteristiche chiave:
::È possibile chiamare funzioni associate tramite un'istanza (tramite punto)?
Questo è possibile (ad esempio, p.new(1.0, 2.0)), ma è estremamente sconsigliato: porta a confusione, poiché la funzione associata non ha accesso all'oggetto corrente e l'istanza viene ignorata. È meglio usare la sintassi Tipo::func().
Esempio di codice:
let p = Point::new(1.0, 2.0); let q = p.new(0.0, 0.0); // Funziona, ma non è una best practice!
Possono i metodi essere asincroni?
Sì. I metodi possono essere dichiarati con la parola chiave async proprio come le funzioni libere:
impl Foo { async fn do_async(&self) { // ... } }
È possibile dichiarare sia metodi che funzioni associate all'interno di un unico blocco impl?
Sì — sono consentite tutte le combinazioni. È anche possibile dichiarare più blocchi impl per un unico tipo.
Un principiante ha dichiarato la funzione new al di fuori di impl e ha cercato di usarla come costruttore, poi ha accidentalmente chiamato tramite un'istanza: p.new(1.0, 2.0).
Vantaggi:
Funziona rapidamente (il compilatore lo consente).
Svantaggi:
Il codice è poco leggibile, difficile da mantenere, complicato nell'uso dei metodi con il corretto possesso di self.
Tutti i metodi e le funzioni associate sono stati dichiarati rigorosamente all'interno di impl, usando le corrette sintassi di invocazione (Tipo::new() per i costruttori, obj.method() per le azioni).
Vantaggi:
Alta leggibilità, conformità alle migliori pratiche.
Svantaggi:
Richiede conoscenza delle idiomi di Rust e attenzione alla sintassi.