ProgrammationDéveloppeur de bibliothèques / Rust Library Developer

Comment le contrôle d'accès aux champs des structures est-il implémenté en Rust, et comment cela est-il lié à la visibilité des modules ? Comment organiser correctement les API publiques pour minimiser les erreurs lors de l'utilisation des structures en dehors du module ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse.

Historique de la question :

Dans des langages comme C++ ou Java, les modificateurs d'accès (public, private, protected) assurent la visibilité des membres de la classe, mais de manière assez flexible — souvent sans empêcher une utilisation erronée de l'API. En Rust, dès ses premières versions, un système de contrôle d'accès stricte et explicitement modulaire a été mis en place pour limiter les "fuites" de détails internes vers l'extérieur.

Problème :

Il est souvent nécessaire de cacher partiellement une structure du code externe : par exemple, les champs sont privés, tandis que seules les méthodes sont exposées. Si l'accès n'est pas restreint, il est possible de compromettre involontairement les invariants de la structure (par exemple, en exposant directement un Vec interne). Publier une structure sans réflexion rend l'API fragile.

Solution :

En Rust, tout est privé par défaut. Le mot-clé pub est utilisé pour l'exportation explicite : pour les structures, on peut déclarer la structure elle-même comme visible, tandis que les champs peuvent rester cachés. Les méthodes peuvent être déclarées pub ou privées individuellement. De plus, des formes non standards telles que pub(crate) ou pub(super) permettent de configurer finement le niveau d'accès.

Exemple de code :

mod domain { pub struct User { pub name: String, age: u32, // champ privé } impl User { pub fn new(name: String, age: u32) -> Self { Self { name, age } } pub fn age(&self) -> u32 { self.age } } } use domain::User; fn main() { let u = User::new("Eve".to_string(), 24); println!("{} {}", u.name, u.age()); // u.age — erreur d'accès ! champ fermé en dehors du module }

Caractéristiques clés :

  • Par défaut, tout est privé, y compris les champs et les fonctions
  • pub expose uniquement les éléments explicitement sélectionnés à l'extérieur
  • pub(crate), pub(super) offrent une configuration flexible des accès pour des projets d'envergure

Questions pièges.

Peut-on rendre une structure pub, mais garder tous ses champs privés ? Comment alors créer ses instances en dehors du module ?

Oui. C'est ce que l'on fait généralement : structure pub, champs privés, création uniquement via des constructeurs publics (par exemple, pub fn new...).

Un champ de structure sera-t-il visible dans le code externe lors de la déclaration pub struct Foo ?

Non, par défaut chaque champ reste privé — il faut spécifiquement indiquer pub pour le champ. pub struct rend uniquement le type visible.

Le pub fonctionne-t-il de la même manière pour les enum ?

Pour enum, pub s'applique à toutes les variantes, mais pour les données associées dans les variantes (par exemple, champ à l'intérieur de Variant(value: T)), il faut aussi indiquer pub si vous voulez rendre les détails accessibles.

Erreurs typiques et anti-patterns

  • Faire pub tous les champs par simplicité, violant ainsi l'encapsulation
  • Essayer d'accéder directement à des champs privés depuis des modules externes (erreur de compilation)
  • Oublier de créer une méthode publique pour construire/modifier la structure si tous les champs sont fermés

Exemple de la vie réelle

Cas négatif

Dans la bibliothèque, la structure était déclarée comme pub struct Config, tous les champs étaient aussi pub — pour que l'utilisateur puisse les "voir". En conséquence, tout code externe pouvait changer arbitrairement l'état, violer les invariants, provoquant des panic de manière inattendue.

Avantages :

  • Ouverture maximale et flexibilité pour l'utilisateur

Inconvénients :

  • Violation de l'encapsulation
  • Difficultés de migration et de versionnage de l'API
  • Multiplication des bugs en raison d'une utilisation incorrecte des champs

Cas positif

Structure Config pub, tous les champs privés. Pour la configuration — uniquement via des méthodes de builder, un constructeur par défaut ou des fonctions setter/getter. En dehors du module, il est impossible de briser les invariants.

Avantages :

  • API propre, invariants sous contrôle
  • Plus facile de maintenir la compatibilité ascendante

Inconvénients :

  • Pour des structures complexes — plus de code (méthodes, constructeurs, tests)