ProgrammationDéveloppeur Rust

Comment fonctionnent les constructeurs et les fonctions associées pour les structures en Rust, et quelle est la différence entre new et with_capacity ? Comment les mettre en œuvre correctement ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse.

Historique de la question

En Rust, il n'y a pas de constructeurs classiques comme dans les langages de programmation orientée objet, mais il existe des fonctions associées (le plus souvent new) qui mettent en œuvre le motif des constructeurs avec initialisation explicite. Les fonctions associées permettent de créer des instances de structures avec des valeurs prédéfinies ou de les créer avec une logique supplémentaire (par exemple, avec une réservation de mémoire).

Problème

Beaucoup commencent à utiliser uniquement new, oubliant d'autres motifs de méthodes de fabrique (comme avec with_capacity), ou mettent mal en œuvre l'initialisation des structures (surtout avec des champs privés/publics), ce qui peut conduire à des erreurs d'invariants.

Solution

Une fonction associée de type new est mise en œuvre via impl, elle peut être le seul point d'entrée pour la création d'objets avec des invariants. Des fonctions comme with_capacity offrent des possibilités supplémentaires, par exemple, allouer de la mémoire à l'avance.

Exemple de code :

pub struct MyBuffer { data: Vec<u8>, } impl MyBuffer { pub fn new() -> Self { MyBuffer { data: Vec::new() } } pub fn with_capacity(cap: usize) -> Self { MyBuffer { data: Vec::with_capacity(cap) } } }

Caractéristiques clés :

  • Les fonctions associées ne reçoivent pas self, fonctionnent comme des méthodes statiques
  • Les méthodes de fabrique publiques aident à établir des invariants
  • Il est possible de mettre en œuvre différents « constructeurs » pour différents scénarios

Questions pièges.

Si je ne mets pas en œuvre new, Rust en générera-t-il un par défaut ?

Non. Il n'y a pas de génération automatique de new. Si la méthode n'est pas explicitement mise en œuvre, elle n'existera pas, même si la structure n'a que des champs publics.

Quelle est la différence fondamentale entre new et Default ?

Default est un constructeur de traits qui ne peut être appelé que là où le type a mis en œuvre ou hérité explicitement de Default. Il est standard pour les algorithmes génériques, mais ne coïncide pas toujours avec new.

Peut-on utiliser les méthodes new et with_capacity pour des structures privées ?

Oui, les méthodes peuvent être publiques ou privées, mais créer un constructeur public pour une structure privée ne permet pas aux utilisateurs de créer des instances de cette structure en dehors du module.

Erreurs typiques et anti-modèles

  • Rendre tous les champs de la structure publics, permettant de créer des valeurs incorrectes en dehors du constructeur
  • Ne pas mettre en œuvre with_capacity pour des collections où les économies de mémoire sont significatives
  • Mettre en œuvre new qui n'initialise pas correctement tous les champs

Exemple de la vie quotidienne

Cas négatif

Structure ouverte sans invariants, tous les champs sont publics :

pub struct User { pub login: String, pub active: bool }

Avantages :

  • Facilité de création d'objets

Inconvénients :

  • Possibilité de créer des valeurs incorrectes (par exemple, un login vide, des champs non initialisés)

Cas positif

Champs privés, création uniquement via new, invariants assurés :

pub struct User { login: String, active: bool } impl User { pub fn new(login: String) -> User { User { login, active: true } } }

Avantages :

  • Protection contre des données incorrectes
  • Possibilité de contrôler à la fois la validation et la logique d'initialisation

Inconvénients :

  • Nécessité d'écrire des méthodes supplémentaires – un peu plus de code