ProgrammazioneSviluppatore Rust Backend

Come sono strutturati i metodi e le funzioni associate in Rust? Come si distinguono tra loro, come si dichiarano e si invocano, e quando si deve usare quale variante?

Supera i colloqui con l'assistente IA Hintsage

Risposta.

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:

  • I metodi accettano il parametro self in diverse varianti di proprietà
  • Le funzioni associate non accettano self, vengono solitamente utilizzate per l'inizializzazione o utility
  • Solo i metodi possono essere invocati su istanze tramite punto, le funzioni associate solo tramite ::

Domande ingannevoli.

È 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.

Errori tipici e anti-pattern

  • Confondere metodi e funzioni associate
  • Lasciare funzioni associate senza specificare l'appartenenza al tipo (non dichiarare in impl)
  • Chiamare funzioni associate tramite un'istanza (tramite punto)

Esempio dalla vita reale

Caso negativo

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.

Caso positivo

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.