跳转到内容

Rust 错误 E0405:cannot find trait in this scope(找不到 trait)

E0405 的意思是:你写了一个 trait 的名字,可 Rust 在那个位置看不到它。最典型的情况:你写了 impl Display for Point,想让自己的结构体能打印出来,却从来没有导入 Display。trait 名字拼错了,或者 trait 定义在别的模块里,也会报同样的错误。这是最好修的错误之一,因为 rustc 通常会直接打印出你需要的那行 use。和 E0412(“cannot find type”,找不到类型)不同,后者从 Rust 1.93 起变成了 E0425,而这个错误码没有变:rustc 1.97 对 trait 报的仍然是 E0405。这个系列的其他错误,请看 Rust 编译错误详解。

error[E0405]: cannot find trait `Display` in this scope
--> src/main.rs:6:6
|
6 | impl Display for Point { // ❌ 从来没有导入过 `Display`
| ^^^^^^^ not found in this scope
|
help: consider importing this trait
|
1 + use std::fmt::Display;
|

第一行的意思是“在这个作用域里找不到 trait Display”。

产生这个错误的程序是这样的:

struct Point {
x: i32,
y: i32,
}
impl Display for Point { // ❌ 从来没有导入过 `Display`
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);
}

^^^^^^^ 指着第 6 行的 Display,标注是“not found in this scope”(在这个作用域里找不到)。Rust 查看了在那里能看到的每一个 trait:你在这个模块里定义的、use 引入的,以及 prelude(预导入)里那一小组。没有一个叫 Display。

help: 告诉你它在哪里:use std::fmt::Display;。1 + 的意思是“把这一行作为新的第 1 行加进去”,也就是加在文件开头。

println! 一直都在用 Display,为什么它会找不到呢?prelude 是每个 Rust 文件都能免费用到的一组名字,里面有 Clone、Iterator、From、Default 等日常 trait,但没有 Display、Error、Hash 和 Write。这些住在各自的模块里(std::fmt、std::error、std::hash、std::io),你要实现它们时就得导入。

这就像菜谱上写着“用 Display 锅”。厨师认识灶台上的那几口锅,可这口锅放在橱柜里,菜谱又没说是哪个橱柜。

Rust 从来不会在整个标准库或整个项目里搜索一个名字。它只看作用域里有什么:你自己的项、你的 use 行,以及 prelude。这让名字的含义可以预测。光是 std 里就有好几个叫 Write 和 Error 的 trait,以后你加的 crate 可能还会带来更多。如果 Rust 替你挑一个,加一个依赖就可能悄悄改变你代码的含义。

所以,你要在文件开头说清楚你指的是哪个 trait,说一次就够了。help 那一行会替你搜索,并建议它找到的 trait。use 的用法见模块,Display 见常用 trait。

当 help 说 consider importing this trait(考虑导入这个 trait)时选这个方法,大多数时候都是这样。把那一行复制到文件开头:

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 在 {}", Point { x: -1, y: 7 });
}

输出:

(3, 4)
Ferris 在 (-1, 7)
▶ 动手试一试

如果 rustc 建议了好几个导入,先看清楚再选。它们是碰巧同名的不同 trait,比如 std::fmt::Write 和 std::io::Write。

修复 2:导入模块,写 fmt::Display

Section titled “修复 2:导入模块,写 fmt::Display”

当你要实现 Display 时选这个方法,因为反正你还需要同一个模块里的 Formatter 和 Result。导入一次 std::fmt,然后在这三个名字前面都写上 fmt::。这是真实 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!("{} 有 {} 个字符", label, label.len());
}

输出:

(3, 4) 有 6 个字符
▶ 动手试一试

实现了 Display,你还能免费得到 .to_string()。你也可以完全不写 use,直接写 impl std::fmt::Display for Point,偶尔用一次的话没问题。

当 rustc 说 a trait with a similar name exists(存在一个名字相近的 trait)时选这个方法。trait 名字区分大小写,所以 shape、Shap 和 Shape 是三个不同的名字:

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 { // ❌ 拼错了:Shap
| ^^^^
|
help: a trait with a similar name exists
|
9 | impl Shape for Square { // ❌ 拼错了:Shap
| +

出错的程序:

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

第 9 行下面的 + 标出了漏掉的那个字母。把它补上:

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());
}

输出:

9
▶ 动手试一试

如果既没有相近的名字,也没有导入建议,说明这个 trait 还不存在。先用 trait Shape { ... } 把它定义出来。参见 trait。

修复 4:从模块里导入你自己的 trait

Section titled “修复 4:从模块里导入你自己的 trait”

当 trait 是你自己写的、但放在另一个模块里时选这个方法。每个模块都有自己的作用域,所以在你导入之前,mod shapes 里的 trait 在 main.rs 顶层是看不到的:

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

出错的程序:

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

加上 use 行,并检查这个 trait 是不是 pub。没有 pub 的话,导入本身就会失败,报 E0603:“trait is private”(trait 是私有的):

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());
}

输出:

4
▶ 动手试一试

来自 crate 的 trait 也一样:impl Serialize for User 需要先写 use serde::Serialize;。参见模块。

在 main 或辅助函数里用 Box<dyn Error>。 没有导入就写 fn run() -> Result<(), Box<dyn Error>>,会报 cannot find trait `Error` in this scope,并附上 help: consider importing this trait 和 use std::error::Error;。修复:加上这一行,或者写成 Box<dyn std::error::Error>。参见自定义错误。

泛型上的 trait 约束。 fn print_all<T: Display>(items: &[T]) 会因为 Display 报同样的 E0405,指向那个约束。修复:use std::fmt::Display;。参见泛型。

Debug 报的是另一个错误码。 没有导入就写 impl Debug for Point 或 T: Debug,报的不是 E0405,而是 E0404:expected trait, found derive macro `Debug` (需要一个 trait,却找到了 derive 宏 Debug)。prelude 里有 #[derive(Debug)] 这个宏,却没有 Debug 这个 trait。修复:use std::fmt::Debug;(rustc 会建议这一行);如果不需要自定义格式,直接用 #[derive(Debug)] 就行。

  • E0425:在这个作用域里找不到值、函数,或者(从 Rust 1.93 起)类型。
  • E0412:“cannot find type”(找不到类型)的旧错误码,现在报为 E0425。
  • E0433:找不到路径的第一部分,比如没写 use std::fmt; 就用 fmt::Display。
  • E0599:找不到方法,常常是因为提供这个方法的 trait 没有导入。
  • E0277:trait 找到了,但你的类型没有实现它。
  • E0404:名字存在,但它不是 trait(比如 Debug 宏),见上文。
  • 所有编译错误:错误页面的完整列表。

小测验

1. 你写了 impl Display for Invoice,结果报 E0405。少了什么?

2. 下面哪个 trait 不写任何 use 就能直接用?

3. 你的 trait Shape 定义在 mod shapes 里,而 impl Shape for Square 写在 main.rs 里。你需要什么?

得分: 0 / 3