En Rust, il n'existe pas de constructeurs traditionnels comme en C++ ou Java, mais pour créer des objets de types, on utilise généralement des fonctions associées (souvent nommées new) et ce que l'on appelle des méthodes de fabrication. Cela est lié à l'histoire du langage, qui accorde une attention particulière à la sécurité et à l'initialisation explicite : seule une fonction écrite et appelée explicitement est responsable de l'initialisation correcte de chaque champ de la structure.
Historique de la question
À l'origine, en Rust, l'initialisation des structures permettait l'affectation directe de tous les champs (système de "struct literal"). Cependant, pour assurer l'invariant, masquer les détails et mettre en œuvre des vérifications supplémentaires, on pratique l'utilisation de méthodes de fabrication (impl SomeStruct { fn new(...) -> Self { ... } }) ou même la généralisation par des templates (patron de construction).
Problème
Les principaux enjeux sont d'éviter les objets partiellement initialisés et de rendre impossible l'utilisation de structures dans un état non valide. Cela est particulièrement critique pour les structures complexes (par exemple, celles liées à des ressources — fichiers, sockets, etc.), où l'initialisation manuelle de tous les champs est source d'erreurs.
Solution
Il est recommandé en Rust de créer des méthodes de fabrication qui retournent un objet complètement initialisé, effectuant au besoin une validation et masquant les détails de l'instanciation.
Exemple de code :
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>, traiter l'erreur en toute sécurité }
Caractéristiques clés :
fn new).Peut-on avoir des champs privés dans une structure, de sorte qu'il soit impossible de créer des instances directement en dehors du module ?
Oui, si tous les champs de la structure sont privés et que l'on propose uniquement des méthodes de fabrication publiques, la structure ne pourra pas être initialisée directement en dehors de son module.
Le méthode de fabrication doit-elle toujours s'appeler new ?
Non, c'est une convention, mais pas une obligation. Pour différentes stratégies d'initialisation, on peut utiliser des noms tels que "with_capacity", "from_config", "from_env", etc.
Les constructeurs peuvent-ils être privés ?
Oui, si la fonction associée est déclarée comme fn new(...) -> Self sans le modificateur pub, elle ne pourra pas être appelée en dehors de ce module. Cela permet, par exemple, d'implémenter un singleton, une factory stricte ou une initialisation masquée.
Utilisation directe d'une structure avec des champs ouverts, sans méthode de fabrication :
struct Connection { fd: i32, timeout: u64, } let c = Connection { fd: -1, timeout: 10 }; // fd: -1 n'est pas valide pour un descripteur !
Avantages :
Inconvénients :
Utilisation de champs privés et d'une méthode de fabrication :
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 } } }
Avantages :
Inconvénients :