In Rust, there are no traditional constructors as in C++ or Java, but object types are usually created using associated functions (often named new) and so-called factory methods. This is related to the language's history, where particular attention is paid to safety and the explicitness of initialization: only an explicitly written and called function is responsible for the correct initialization of each field of the structure.
Background
Initially, Rust allowed direct assignment of all fields during structure initialization (the so-called "struct literal" syntax). However, to ensure invariance, hide details, and implement additional checks, it is common to use factory methods (impl SomeStruct { fn new(...) -> Self { ... } }) or even generalization through templates (builder pattern).
Problem
The main tasks are to prevent partially initialized objects and make it impossible to use structures with invalid states. This is especially critical for complex structures (for example, those related to resources — files, sockets, etc.), where manually initializing all fields is fraught with errors.
Solution
In Rust, it is recommended to create factory methods that return a fully initialized object, perform validation if necessary, and hide the instantiation details.
Code example:
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>, safely handle the error }
Key features:
fn new).Can private fields be made in a structure to prevent instances from being created directly outside the module?
Yes, if all fields of the structure are made private and only public factory methods are provided, the structure cannot be initialized directly outside its module.
Does the factory method always have to be named new?
No, this is a convention, but not an obligation. Different strategies for initialization use names like "with_capacity", "from_config", "from_env", and so on.
Can there be private constructors?
Yes, if the associated function is declared as fn new(...) -> Self without the pub modifier, it cannot be called outside this module. This allows, for example, to implement a singleton, enforce a factory, or hidden initialization.
Direct use of a structure with open fields, without a factory method:
struct Connection { fd: i32, timeout: u64, } let c = Connection { fd: -1, timeout: 10 }; // fd: -1 is invalid for a descriptor!
Pros:
Cons:
Using private fields and a factory method:
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 } } }
Pros:
Cons: