In Rust zijn er geen traditionele constructeurs zoals in C++ of Java, maar voor het maken van objecten van types worden meestal geassocieerde functies (vaak met de naam new) en zogenaamde fabrieksmethoden gebruikt. Dit is te wijten aan de geschiedenis van de taal, waar veel aandacht wordt besteed aan veiligheid en duidelijkheid van initialisatie: alleen een expliciet geschreven en aangeroepen functie is verantwoordelijk voor de correcte initialisatie van elk veld van de structuur.
Achtergrond van de vraag
Oorspronkelijk stond Rust toe dat structuren werden geïnitialiseerd door directe toewijzing van alle velden (de zogenaamde "struct literal" syntaxis). Echter, om invariantie te waarborgen, details te verbergen en extra controles te implementeren, wordt het gebruik van fabrieksmethoden (impl SomeStruct { fn new(...) -> Self { ... } }) of zelfs generiek maken via sjablonen (builder pattern) beoefend.
Probleem
De belangrijkste taken zijn om te voorkomen dat gedeeltelijk geïnitialiseerde objecten worden gemaakt en om het gebruik van structuren met een ongeldige status onmogelijk te maken. Dit is vooral kritiek voor complexe structuren (bijvoorbeeld die gerelateerd zijn aan bronnen — bestanden, sockets, enz.), waar handmatige initialisatie van alle velden gevoelig is voor fouten.
Oplossing
In Rust wordt aanbevolen om fabrieksmethoden te maken die een volledig geïnitialiseerd object retourneren, indien nodig validatie uitvoeren en details van instantiatie verbergen.
Voorbeeldcode:
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>, veilig om de fout af te handelen }
Belangrijke kenmerken:
fn new).Kan je privévelden in een structuur maken, zodat je geen instanties rechtstreeks buiten het module kunt maken?
Ja, als je alle velden van de structuur privé maakt en alleen openbare fabrieksmethoden aanbiedt, kan de structuur niet rechtstreeks buiten zijn module worden geïnitialiseerd.
Moet de fabrieksmethode altijd new heten?
Nee, dit is een conventie, maar geen verplichting. Voor verschillende initiële strategieën worden namen zoals "with_capacity", "from_config", "from_env" enzovoort gebruikt.
Kunnen er privéconstructeurs zijn?
Ja, als de geassocieerde functie wordt verklaard als fn new(...) -> Self zonder de pub-modificator, kan deze niet buiten deze module worden aangeroepen. Dit maakt het bijvoorbeeld mogelijk om singleton, enforce factory of verborgen initialisatie te implementeren.
Direct gebruik van een structuur met open velden, zonder fabrieksmethode:
struct Connection { fd: i32, timeout: u64, } let c = Connection { fd: -1, timeout: 10 }; // fd: -1 is ongeldig voor een descriptor!
Voordelen:
Nadelen:
Gebruik van privévelden en een fabrieksmethode:
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 } } }
Voordelen:
Nadelen: