In Rust gibt es keine traditionellen Konstruktoren wie in C++ oder Java, aber zur Erstellung von Objekten von Typen verwendet man normalerweise assoziierte Funktionen (häufig mit dem Namen new) und sogenannte Fabrikmethoden. Dies hängt mit der Geschichte der Sprache zusammen, in der besonderes Augenmerk auf Sicherheit und Eindeutigkeit der Initialisierung gelegt wird: Nur eine explizit geschriebene und aufgerufene Funktion ist für die korrekte Initialisierung jedes Feldes der Struktur verantwortlich.
Geschichte des Themas
Ursprünglich erlaubte die Initialisierung von Strukturen in Rust die direkte Zuweisung aller Felder (sogenannter "struct literal" Syntax). Um jedoch die Invarianz zu gewährleisten, Details zu verbergen und zusätzliche Überprüfungen zu implementieren, wird die Verwendung von Fabrikmethoden (impl SomeStruct { fn new(...) -> Self { ... } }) oder sogar Generierung durch Vorlagen (Builder-Pattern) praktiziert.
Das Problem
Die Hauptaufgaben bestehen darin, nicht teilweise initialisierte Objekte zuzulassen und die Verwendung von Strukturen mit ungültigem Zustand unmöglich zu machen. Dies ist besonders kritisch für komplexe Strukturen (z.B. im Zusammenhang mit Ressourcen – Dateien, Sockets usw.), bei denen die manuelle Initialisierung aller Felder fehleranfällig ist.
Die Lösung
In Rust wird empfohlen, Fabrikmethoden zu erstellen, die ein vollständig initialisiertes Objekt zurückgeben, bei Bedarf Validierungen durchführen und die Details der Instanziierung verbergen.
Beispielcode:
struct User { username: String, age: u8, } impl User { pub fn new(username: String, age: u8) -> Option<Self> { if age >= 18 { Some(Self { username, age }) } else { None } } } fn main() { let user = User::new("Alice".to_string(), 20); // user: Option<User>, sicherer Umgang mit Fehlern }
Wesentliche Merkmale:
fn new).Kann man private Felder in einer Struktur erstellen, sodass man keine Instanzen direkt außerhalb des Moduls erstellen kann?
Ja, wenn alle Felder der Struktur privat gemacht werden und nur öffentliche Fabrikmethoden bereitgestellt werden, kann die Struktur nicht direkt außerhalb ihres Moduls initialisiert werden.
Muss die Fabrikmethode immer new heißen?
Nein, das ist eine Konvention, aber keine Verpflichtung. Für verschiedene Initialisierungsstrategien werden Namen wie "with_capacity", "from_config", "from_env" usw. verwendet.
Können Konstruktoren privat sein?
Ja, wenn die assoziierte Funktion als fn new(...) -> Self ohne das pub-Modifikator deklariert ist, kann sie außerhalb dieses Moduls nicht aufgerufen werden. Dies ermöglicht es beispielsweise, Singletons, enforce factories oder eine versteckte Initialisierung zu implementieren.
Direkte Verwendung einer Struktur mit offenen Feldern, ohne Fabrikmethode:
struct Connection { fd: i32, timeout: u64, } let c = Connection { fd: -1, timeout: 10 }; // fd: -1 ist ungültig für einen Deskriptor!
Vorteile:
Nachteile:
Verwendung privater Felder und einer Fabrikmethode:
pub struct Connection { fd: i32, timeout: u64, } impl Connection { pub fn new(fd: i32, timeout: u64) -> Option<Self> { if fd >= 0 { Some(Self { fd, timeout }) } else { None } } }
Vorteile:
Nachteile: