Aller au contenu

Erreur Rust E0411 : cannot find type Self in this scope (Self hors d'un impl)

E0411 veut dire que vous avez écrit Self à un endroit où Rust ne peut pas savoir ce qu’il représente. Self est un raccourci pour « le type dont parle ce bloc impl (ou ce trait) », il ne marche donc qu’à l’intérieur d’un impl, d’un trait ou d’une définition de type. La cause habituelle : un constructeur comme fn new() -> Self écrit au premier niveau d’un fichier, ou une méthode que vous avez copiée hors de son bloc impl. C’est une erreur courante et vite corrigée. Contrairement à E0412, devenue E0425 avec Rust 1.93, ce code n’a pas changé : rustc 1.97 affiche toujours E0411. Cette page fait partie du guide des erreurs du compilateur Rust.

error[E0411]: cannot find type `Self` in this scope
--> src/main.rs:5:21
|
5 | fn new_counter() -> Self { // ❌ `Self` hors d'un `impl`
| ----------- ^^^^ `Self` is only available in impls, traits, and type definitions
| |
| `Self` not allowed in a function

Le compilateur vous dit : « impossible de trouver le type Self dans cette portée ». La section « Ce que ça veut dire » ci-dessous traduit chaque ligne.

Voici le programme qui la provoque :

struct Counter {
count: u32,
}
fn new_counter() -> Self { // ❌ `Self` hors d'un `impl`
Counter { count: 0 }
}
fn main() {
let c = new_counter();
println!("{}", c.count);
}

rustc pointe deux choses sur la ligne 5 :

  1. Les ^^^^ sous Self : « Self is only available in impls, traits, and type definitions » (Self n’est disponible que dans les impl, les traits et les définitions de type). C’est la règle, en une ligne.
  2. Les ----- sous new_counter : « Self not allowed in a function » (Self n’est pas autorisé dans une fonction). C’est la raison ici : new_counter est une fonction ordinaire, qui ne fait partie d’aucun impl.

Vous savez peut-être que new_counter renvoie un Counter, puisque vous venez d’écrire Counter { count: 0 }. Mais Rust ne lit pas le corps de la fonction pour deviner ce que veut dire Self. Self ne prend son sens que grâce à un impl Counter { ... } ou un trait ... { ... } qui l’entoure, et ici il n’y en a aucun.

C’est comme le mot « ici » sur un post-it. Sur un plan, « vous êtes ici » a un sens. Sur un post-it posé sur la table de la cuisine, personne ne sait où est « ici ».

Self n’est pas un type à part entière. C’est un emplacement réservé que chaque impl ou trait remplit : dans impl Counter, Self veut dire Counter ; dans impl Timer, il veut dire Timer ; dans un trait, il veut dire « le type, quel qu’il soit, qui implémente ce trait ». C’est ce qui le rend utile : vous pouvez renommer la struct, ou copier une méthode dans un autre impl, et Self suit le mouvement.

Une fonction libre n’appartient à aucun type, il n’y a donc rien pour remplir l’emplacement. Rust pourrait essayer de deviner d’après le corps, mais alors une signature dépendrait du code qu’elle contient, et Rust vérifie volontairement les signatures toutes seules. Voir Les fonctions de type constructeur pour la place habituelle de fn new() -> Self.

Solution 1 : déplacer la fonction dans un bloc impl

Section intitulée « Solution 1 : déplacer la fonction dans un bloc impl »

Choisissez-la la plupart du temps. Une fonction qui crée un type ou travaille avec lui a sa place dans le bloc impl de ce type, et là, Self fonctionne comme vous l’attendiez. Appelez-la avec Counter::new() :

struct Counter {
count: u32,
}
impl Counter {
fn new() -> Self {
Self { count: 0 }
}
fn increment(&mut self) {
self.count += 1;
}
}
fn main() {
let mut c = Counter::new();
c.increment();
c.increment();
println!("{}", c.count);
}

Sortie :

2
▶ Essayez par vous-même

Dans l’impl, vous pouvez utiliser Self à la fois comme type (-> Self) et pour construire une valeur (Self { count: 0 }). Voir Ajouter du comportement avec des méthodes.

Solution 2 : écrire le nom du type au lieu de Self

Section intitulée « Solution 2 : écrire le nom du type au lieu de Self »

Choisissez-la quand la fonction doit rester une fonction libre, par exemple une fonction d’aide qui construit plusieurs types, ou une fonction qui vit dans un autre module. Écrivez simplement le vrai type, Counter, là où vous aviez écrit Self :

struct Counter {
count: u32,
}
fn new_counter(start: u32) -> Counter {
Counter { count: start }
}
fn reset(c: &mut Counter) {
c.count = 0;
}
fn main() {
let mut c = new_counter(10);
println!("{}", c.count);
reset(&mut c);
println!("{}", c.count);
}

Sortie :

10
0
▶ Essayez par vous-même

C’est aussi la correction pour un fn reset(c: &mut Self) copié hors d’un impl : chaque Self de la signature et du corps devient Counter.

Solution 3 : utiliser un type générique si vous vouliez dire « n’importe quel type »

Section intitulée « Solution 3 : utiliser un type générique si vous vouliez dire « n’importe quel type » »

Choisissez-la quand vous avez utilisé Self pour dire « le type que l’appelant voudra », ce qui est le travail d’un générique T, pas de Self. Déclarez <T> avec les traits dont la fonction a besoin. Ici, T: Default permet à la fonction de créer une valeur de départ de n’importe quel type :

fn fresh_pair<T: Default>() -> (T, T) {
(T::default(), T::default())
}
fn main() {
let numbers: (u32, u32) = fresh_pair();
let words: (String, String) = fresh_pair();
println!("{:?}", numbers);
println!("{:?}", words);
}

Sortie :

(0, 0)
("", "")
▶ Essayez par vous-même

Si vous voulez que chaque type décide lui-même comment il se construit, mettez la fonction dans un trait : trait Reset { fn fresh() -> Self; }. Dans un trait, Self veut dire « le type qui l’implémente ». Voir Les génériques et Définir un trait.

Self { ... } dans main ou dans une fonction libre. let c = Self { count: 0 }; donne E0411 avec un titre un peu différent : cannot find struct, variant or union type `Self` in this scope (impossible de trouver une struct, une variante ou une union Self dans cette portée). Correction : écrivez Counter { count: 0 }, ou appelez Counter::new().

Une fonction d’aide imbriquée dans une méthode. Dans impl Counter, une méthode qui contient sa propre fn helper() -> Self donne un autre code, E0401 : can't use `Self` from outer item (impossible d’utiliser le Self de l’élément extérieur). Une fonction dans une fonction n’hérite pas de l’impl extérieur. Correction : écrivez Counter dans la fonction intérieure, utilisez une closure (fonction anonyme), ou remontez helper dans l’impl comme méthode à part entière.

Confondre Self et self. Self avec une majuscule, c’est le type ; self en minuscules, c’est la valeur sur laquelle une méthode est appelée. Utiliser self en dehors d’une méthode est une autre erreur, E0424. Correction dans les deux cas : vérifiez que le code se trouve dans le bon bloc impl.

  • E0424 : self (en minuscules, la valeur) utilisé en dehors d’une méthode.
  • E0425 : impossible de trouver une valeur, une fonction ou (depuis Rust 1.93) un type au nom ordinaire.
  • E0412 : l’ancien code pour « cannot find type », signalé aujourd’hui en E0425.
  • E0405 : impossible de trouver un trait, en général à cause d’une ligne use oubliée.
  • E0401 : le Self d’un impl extérieur utilisé dans une fonction imbriquée, présenté plus haut.
  • Toutes les erreurs du compilateur : la liste complète des pages d’erreurs.

Petit quiz

1. Où pouvez-vous utiliser Self comme type ?

2. fn make_user() -> Self au premier niveau du fichier donne E0411. Quelle est la correction habituelle ?

3. Dans impl Counter, que crée Self { count: 0 } ?

Score : 0 / 3