Auto-déréférencement en Rust : règles, exemples et "magie" sous le capot

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Auto-déréférencement en Rust : comment ça fonctionne et pourquoi ce n'est pas de la magie

\n\n

Si vous avez déjà écrit en Rust, vous avez sûrement remarqué que le compilateur permet d'appeler des méthodes sur des références et des pointeurs intelligents comme si vous travailliez avec la valeur elle-même. Ce n'est ni de la magie ni un bug — c'est un mécanisme strictement défini appelé auto-déréférencement (auto-dereferencing).

\n\n

Sur Stack Overflow, la question « What are Rust's exact auto-dereferencing rules? » a recueilli des milliers de vues — et ce n'est pas pour rien. Comprendre ces règles transforme le code Rust d'un « ensemble d'erreurs étranges » en un outil élégant et prévisible. Regardons sous le capot.

\n\n

Qu'est-ce que l'auto-déréférencement et pourquoi est-il nécessaire ?

\n\n

En Rust, nous avons des références (&T, &mut T) et des pointeurs intelligents (Box<T>, Rc<T>, Arc<T>). Lorsque vous écrivez my_box.some_method(), le compilateur applique automatiquement le déréférencement pour accéder à la valeur interne T. Sans ce mécanisme, vous devriez écrire (*my_box).some_method() ou (**my_ref).some_method() — ce qui devient rapidement fastidieux et encombre le code.

\n\n

L'auto-déréférencement rend le code propre, mais il est important d'en comprendre les limites et les règles précises pour ne pas tomber dans un piège.

\n\n

Les règles précises de l'auto-déréférencement en Rust

\n\n

Examinons la spécification officielle. Lorsque le compilateur rencontre une expression de la forme x.method(), il essaie de trouver une méthode nommée method pour le type T, où T est le type de x. Si la méthode n'est pas trouvée, le compilateur commence le processus de déréférencement automatique (ou de prise de référence).

\n\n

Voici la séquence d'étapes que le compilateur exécute (simplifiée) :

\n\n
    \n
  1. Étape 1 : Essayer de trouver la méthode pour le type T (tel quel).
  2. \n
  3. Étape 2 : Si non trouvée — essayer de trouver la méthode pour &T ou &mut T (prise de référence).
  4. \n
  5. Étape 3 : Si non trouvée — appliquer Deref, c'est-à-dire déréférencer une fois (obtenir U à partir de T).
  6. \n
  7. Étape 4 : Répéter les étapes 1 à 3 pour le nouveau type U.
  8. \n
  9. Étape 5 : Si après plusieurs déréférencements la méthode n'est toujours pas trouvée — erreur de compilation.
  10. \n
\n\n

Important : le compilateur ne fait pas de chaîne infinie. Il applique le déréférencement jusqu'à ce qu'il trouve la méthode ou qu'il atteigne un type qui n'implémente pas Deref.

\n\n

Exemples : du simple au complexe

\n\n

Regardons un code qui démontre ces règles en action.

\n\n

Exemple 1 : Référence de base

\n\n
struct Point {\n    x: i32,\n    y: i32,\n}\n\nimpl Point {\n    fn distance_from_origin(&self) -> f64 {\n        ((self.x.pow(2) + self.y.pow(2)) as f64).sqrt()\n    }\n}\n\nfn main() {\n    let p = Point { x: 3, y: 4 };\n    let r = &p; // &Point\n    println!("{}", r.distance_from_origin()); // 5.0\n}
\n\n

Ici, r est un &Point. La méthode distance_from_origin prend &self. Le compilateur voit : r est un &Point. Il essaie de trouver la méthode pour &Point — il la trouve ! Tout fonctionne sans déréférencement explicite.

\n\n

Exemple 2 : Double référence

\n\n
fn main() {\n    let p = Point { x: 3, y: 4 };\n    let r1 = &p;     // &Point\n    let r2 = &r1;    // &&Point\n    println!("{}", r2.distance_from_origin()); // 5.0\n}
\n\n

Le type de r2 est &&Point. Le compilateur essaie de trouver la méthode pour &&Point — non. Il essaie de prendre une référence : &&&Point — non plus. Ensuite, il applique Deref une fois : il obtient &Point. Pour

Blogs

Book Recommendations