Geschichte des Themas
Rust entleiht das Konzept von Methoden aus objektorientierten Sprachen, implementiert diese jedoch anders: Anstatt des gewohnten this oder self akzeptieren Methoden explizit einen self-Parameter. Assoziierte Funktionen hingegen entstanden als Alternative zu statischen Methoden in anderen Programmiersprachen – sie sind mit dem Typ verbunden, jedoch nicht mit einem bestimmten Wert.
Problem
Oft werden Methoden, assoziierte Funktionen und freie Funktionen verwechselt. Es ist nicht eindeutig, wann eine Methode mit self und wann eine assoziierte Funktion ohne self verwendet werden sollte. Es gibt Fragen zu Sichtbarkeit, Auto-Dereferenzierung und Eigentumsübergabe.
Lösung
Methoden in Rust werden mit dem ersten Parameter self/ &self/ &mut self innerhalb eines impl-Blocks deklariert (üblicherweise für struct oder enum). Sie werden auf einer Instanz aufgerufen: object.method(). Assoziierte Funktionen (z.B. new, from) werden ebenfalls innerhalb von impl deklariert, jedoch ohne den ersten Parameter self und werden über zwei Doppelpunkte aufgerufen: Type::function().
Beispielcode:
struct Point { x: f64, y: f64, } impl Point { // Assoziierte Funktion (Konstruktor) fn new(x: f64, y: f64) -> Self { Self { x, y } } // Methode: benötigt 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
Schlüsselfunktionen:
::Kann man assoziierte Funktionen über eine Instanz (über den Punkt) aufrufen?
Das ist möglich (z.B. p.new(1.0, 2.0)), wird jedoch dringend abgeraten: Es führt in die Irre, da die assoziierte Funktion keinen Zugriff auf das aktuelle Objekt hat und die Instanz ignoriert übergeben wird. Es ist besser, die Syntax Type::func() zu verwenden.
Beispielcode:
let p = Point::new(1.0, 2.0); let q = p.new(0.0, 0.0); // Funktioniert, ist aber nicht best practice!
Können Methoden asynchron sein?
Ja. Methoden können mit dem Schlüsselwort async deklariert werden, genau wie freie Funktionen:
impl Foo { async fn do_async(&self) { // ... } }
Kann man in einem einzigen impl-Block sowohl Methoden als auch assoziierte Funktionen deklarieren?
Ja – beliebige Kombinationen sind zulässig. Es ist auch möglich, mehrere impl-Blöcke für einen Typ zu deklarieren.
Ein Neuling hat die Funktion new außerhalb von impl deklariert und versuchte, sie als Konstruktor zu verwenden, und rief sie dann versehentlich über eine Instanz auf: p.new(1.0, 2.0).
Vorteile:
Funktioniert schnell (Kompilierer erlaubt es).
Nachteile:
Der Code ist unleserlich, schwer wartbar, schwer zu verwenden mit richtigen Eigentumsübergaben für self.
Alle Methoden und assoziierten Funktionen sind strikt innerhalb von impl deklariert, die richtigen Aufrufsyntaxen werden verwendet (Type::new() für Konstruktoren, obj.method() für Aktionen).
Vorteile:
Hohe Lesbarkeit, entspricht den Best Practices.
Nachteile:
Erfordert Kenntnisse der Rust-Ideome und Aufmerksamkeit auf die Syntax.