Aller au contenu

Erreur Rust E0405 : cannot find trait in this scope (trait introuvable)

E0405 veut dire que vous avez nommé un trait que Rust ne voit pas depuis cet endroit. Le cas classique : vous écrivez impl Display for Point pour rendre votre struct affichable, mais vous n’avez jamais importé Display. Une faute de frappe dans le nom du trait, ou un trait qui vit dans un autre module, donne la même erreur. C’est l’une des erreurs les plus simples à corriger, parce que rustc affiche en général la ligne use exacte dont vous avez besoin. Contrairement à E0412 (« cannot find type »), devenue E0425 avec Rust 1.93, ce code n’a pas changé : rustc 1.97 affiche toujours E0405 pour les traits. Cette page fait partie du guide des erreurs du compilateur Rust.

error[E0405]: cannot find trait `Display` in this scope
--> src/main.rs:6:6
|
6 | impl Display for Point { // ❌ `Display` n'a jamais été importé
| ^^^^^^^ not found in this scope
|
help: consider importing this trait
|
1 + use std::fmt::Display;
|

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

Voici le programme qui la provoque :

struct Point {
x: i32,
y: i32,
}
impl Display for Point { // ❌ `Display` n'a jamais été importé
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}
fn main() {
let p = Point { x: 3, y: 4 };
println!("{}", p);
}

Les ^^^^^^^ pointent Display à la ligne 6, avec l’étiquette « not found in this scope » (introuvable dans cette portée). Rust a regardé tous les traits visibles depuis cet endroit : ceux que vous avez définis dans ce module, ceux qu’apportent vos lignes use, et le petit ensemble du prélude. Aucun ne s’appelle Display.

Le help: vous dit où il se trouve : use std::fmt::Display; (« consider importing this trait », pensez à importer ce trait). Le 1 + veut dire « ajoutez ceci comme nouvelle ligne 1 », tout en haut du fichier.

Pourquoi Display manque-t-il alors que println! s’en sert tout le temps ? Le prélude, la liste de noms que chaque fichier Rust reçoit gratuitement, contient Clone, Iterator, From, Default et d’autres traits de tous les jours, mais pas Display, Error, Hash ni Write. Ceux-là vivent dans leurs modules (std::fmt, std::error, std::hash, std::io), et vous les importez quand vous les implémentez.

C’est comme une recette qui dit « prenez la poêle Display ». Le cuisinier connaît les poêles posées sur la cuisinière, mais celle-ci est rangée dans un placard, et la recette ne dit pas lequel.

Rust ne fouille jamais toute la bibliothèque standard, ni tout votre projet, pour trouver un nom. Il regarde seulement ce qui est dans la portée : vos propres éléments, vos lignes use et le prélude. Ça rend les noms prévisibles. Il existe plusieurs traits appelés Write et Error rien que dans std, et une crate ajoutée plus tard pourrait en apporter d’autres. Si Rust en choisissait un à votre place, ajouter une dépendance pourrait changer en douce le sens de votre code.

Alors vous dites quel trait vous voulez, une fois, en haut du fichier. La ligne d’aide fait la recherche pour vous et propose le trait qu’elle a trouvé. Voir use : raccourcir les chemins pour comprendre comment fonctionne use, et Les traits courants pour Display.

Solution 1 : ajouter la ligne use que rustc propose

Section intitulée « Solution 1 : ajouter la ligne use que rustc propose »

Choisissez-la quand l’aide dit consider importing this trait, c’est-à-dire la plupart du temps. Copiez la ligne en haut du fichier :

use std::fmt::Display;
struct Point {
x: i32,
y: i32,
}
impl Display for Point {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}
fn main() {
let p = Point { x: 3, y: 4 };
println!("{}", p);
println!("Ferris est en {}", Point { x: -1, y: 7 });
}

Sortie :

(3, 4)
Ferris est en (-1, 7)
▶ Essayez par vous-même

Si rustc propose plusieurs imports, lisez-les avant de choisir. Ce sont des traits différents qui portent par hasard le même nom, comme std::fmt::Write et std::io::Write.

Solution 2 : importer le module et écrire fmt::Display

Section intitulée « Solution 2 : importer le module et écrire fmt::Display »

Choisissez-la quand vous implémentez Display, parce que vous avez de toute façon besoin de Formatter et de Result, qui viennent du même module. Importez std::fmt une fois et écrivez fmt:: devant les trois. C’est le style que vous verrez le plus dans du vrai code Rust :

use std::fmt;
struct Point {
x: i32,
y: i32,
}
impl fmt::Display for Point {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}
fn main() {
let p = Point { x: 3, y: 4 };
let label = p.to_string();
println!("{} fait {} caractères", label, label.len());
}

Sortie :

(3, 4) fait 6 caractères
▶ Essayez par vous-même

Implémenter Display vous donne aussi .to_string() gratuitement. Vous pouvez aussi vous passer complètement du use et écrire impl std::fmt::Display for Point ; ça convient très bien pour un usage ponctuel.

Solution 3 : corriger l’orthographe du nom du trait

Section intitulée « Solution 3 : corriger l’orthographe du nom du trait »

Choisissez-la quand rustc dit a trait with a similar name exists (un trait au nom proche existe). Les noms de traits sont sensibles à la casse, donc shape, Shap et Shape sont trois noms différents :

error[E0405]: cannot find trait `Shap` in this scope
--> src/main.rs:9:6
|
1 | trait Shape {
| ----------- similarly named trait `Shape` defined here
...
9 | impl Shap for Square { // ❌ faute de frappe : Shap
| ^^^^
|
help: a trait with a similar name exists
|
9 | impl Shape for Square { // ❌ faute de frappe : Shap
| +

Le programme qui ne compile pas :

trait Shape {
fn area(&self) -> f64;
}
struct Square {
side: f64,
}
impl Shap for Square { // ❌ faute de frappe : Shap
fn area(&self) -> f64 {
self.side * self.side
}
}
fn main() {
let s = Square { side: 3.0 };
println!("{}", s.area());
}

Le + sous la ligne 9 montre la lettre qui manque. Ajoutez-la :

trait Shape {
fn area(&self) -> f64;
}
struct Square {
side: f64,
}
impl Shape for Square {
fn area(&self) -> f64 {
self.side * self.side
}
}
fn main() {
let s = Square { side: 3.0 };
println!("{}", s.area());
}

Sortie :

9
▶ Essayez par vous-même

S’il n’y a ni nom proche ni suggestion d’import, c’est que le trait n’existe pas encore. Définissez-le d’abord avec trait Shape { ... }. Voir Définir un trait.

Solution 4 : importer votre propre trait depuis son module

Section intitulée « Solution 4 : importer votre propre trait depuis son module »

Choisissez-la quand le trait est à vous, mais qu’il vit dans un autre module. Chaque module a sa propre portée, donc un trait de mod shapes n’est pas visible en haut de main.rs tant que vous ne l’importez pas :

error[E0405]: cannot find trait `Shape` in this scope
--> src/main.rs:11:6
|
11 | impl Shape for Square { // ❌ `Shape` vit dans `mod shapes`
| ^^^^^ not found in this scope
|
help: consider importing this trait
|
1 + use crate::shapes::Shape;
|

Le programme qui ne compile pas :

mod shapes {
pub trait Shape {
fn area(&self) -> f64;
}
}
struct Square {
side: f64,
}
impl Shape for Square { // ❌ `Shape` vit dans `mod shapes`
fn area(&self) -> f64 {
self.side * self.side
}
}
fn main() {
println!("{}", Square { side: 2.0 }.area());
}

Ajoutez la ligne use, et vérifiez que le trait est pub. Sans pub, c’est l’import lui-même qui échoue, avec E0603, « trait is private » (le trait est privé) :

mod shapes {
pub trait Shape {
fn area(&self) -> f64;
}
}
use crate::shapes::Shape;
struct Square {
side: f64,
}
impl Shape for Square {
fn area(&self) -> f64 {
self.side * self.side
}
}
fn main() {
println!("{}", Square { side: 2.0 }.area());
}

Sortie :

4
▶ Essayez par vous-même

C’est pareil pour les traits venant de crates : impl Serialize for User demande d’abord use serde::Serialize;. Voir Tout est privé par défaut.

Box<dyn Error> dans main ou une fonction d’aide. fn run() -> Result<(), Box<dyn Error>> sans import donne cannot find trait `Error` in this scope, avec help: consider importing this trait et use std::error::Error;. Correction : ajoutez cette ligne, ou écrivez Box<dyn std::error::Error>. Voir Les erreurs personnalisées.

Les contraintes de trait sur les génériques. fn print_all<T: Display>(items: &[T]) donne la même E0405 pour Display, en pointant la contrainte. Correction : use std::fmt::Display;. Voir Les génériques.

Debug donne un autre code. impl Debug for Point ou T: Debug sans import, ce n’est pas E0405 mais E0404 : expected trait, found derive macro `Debug` (trait attendu, macro derive Debug trouvée). Le prélude contient la macro #[derive(Debug)], mais pas le trait Debug. Correction : use std::fmt::Debug; (rustc le propose), ou simplement #[derive(Debug)] si vous n’avez pas besoin d’un format personnalisé.

  • E0425 : impossible de trouver une valeur, une fonction ou (depuis Rust 1.93) un type dans cette portée.
  • E0412 : l’ancien code pour « cannot find type », signalé aujourd’hui en E0425.
  • E0433 : impossible de trouver la première partie d’un chemin, comme fmt::Display sans use std::fmt;.
  • E0599 : méthode introuvable, souvent parce que le trait qui fournit la méthode n’est pas importé.
  • E0277 : le trait a été trouvé, mais votre type ne l’implémente pas.
  • E0404 : un nom qui existe, mais qui n’est pas un trait (comme la macro Debug), présenté plus haut.
  • Toutes les erreurs du compilateur : la liste complète des pages d’erreurs.

Petit quiz

1. Vous écrivez impl Display for Invoice et obtenez E0405. Qu'est-ce qui manque ?

2. Lequel de ces traits pouvez-vous utiliser sans aucune ligne use ?

3. Votre trait Shape est défini dans mod shapes, et impl Shape for Square est dans main.rs. De quoi avez-vous besoin ?

Score : 0 / 3