跳转到内容

Rust 错误 E0411:cannot find type Self in this scope(在 impl 外使用 Self)

E0411 的意思是:你在 Rust 看不出 Self 指什么的地方写了 Self。Self 是“这个 impl 块(或 trait)所针对的类型”的简写,所以它只能用在 impl、trait 或类型定义里面。最常见的原因是把 fn new() -> Self 这样的构造函数写在了文件顶层,或者把一个方法从它的 impl 块里复制了出来。这个错误很常见,改起来也快。和 E0412 不同(它从 Rust 1.93 起变成了 E0425),这个错误码没有变:rustc 1.97 报的仍然是 E0411。这个系列的其他错误,请看 Rust 编译错误详解。

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

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

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

struct Counter {
count: u32,
}
fn new_counter() -> Self { // ❌ 在 `impl` 外面用了 `Self`
Counter { count: 0 }
}
fn main() {
let c = new_counter();
println!("{}", c.count);
}

rustc 在第 5 行指出了两处:

  1. Self 下面的 ^^^^:“Self is only available in impls, traits, and type definitions”(Self 只能用在 impl、trait 和类型定义里)。规则就这一句话。
  2. new_counter 下面的 -----:“Self not allowed in a function”(函数里不允许用 Self)。这就是这里出错的原因:new_counter 是一个普通函数,不属于任何 impl。

你也许知道 new_counter 返回的是 Counter,因为你刚写了 Counter { count: 0 }。但 Rust 不会去读函数体来猜 Self 是什么。Self 的含义只来自包住它的 impl Counter { ... } 或 trait ... { ... },而这里一个都没有。

这就像便利贴上写着“这里”。在地图上,“你在这里”是有意义的;可一张随手放在餐桌上的便利贴,谁也不知道“这里”是哪里。

Self 本身不是一个类型,而是一个占位符,由每个 impl 或 trait 来填上:在 impl Counter 里,Self 就是 Counter;在 impl Timer 里,它就是 Timer;在 trait 里,它表示“实现这个 trait 的那个类型”。这正是它好用的地方:你给结构体改名,或者把方法复制到另一个 impl 里,Self 都会跟着变。

普通函数不属于任何类型,所以没有东西可以填进去。Rust 本可以试着从函数体里猜,但那样一来,函数签名就要依赖函数体里的代码了,而 Rust 有意只凭签名本身来检查签名。fn new() -> Self 通常该放在哪里,见结构体。

大多数时候选这个方法。创建某个类型、或者围绕某个类型工作的函数,本来就该放在这个类型的 impl 块里,在那里 Self 会按你预期的那样工作。用 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);
}

输出:

2
▶ 动手试一试

在 impl 里面,Self 既可以当类型用(-> Self),也可以用来构造值(Self { count: 0 })。参见结构体。

如果这个函数应该保持为普通函数,比如一个要构造好几种类型的辅助函数,或者它放在另一个模块里,就选这个方法。在你写 Self 的地方,直接写出真正的类型 Counter:

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

输出:

10
0
▶ 动手试一试

从 impl 里复制出来的 fn reset(c: &mut Self) 也用这个方法修:签名和函数体里的每个 Self 都改成 Counter。

修复 3:如果你想表达“任意类型”,就用泛型

Section titled “修复 3:如果你想表达“任意类型”,就用泛型”

如果你用 Self 想表达的是“调用者想要的任意类型”,那该用泛型 T,而不是 Self。声明 <T>,并写上函数需要的 trait。这里的 T: Default 让函数能为任意类型创建一个初始值:

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

输出:

(0, 0)
("", "")
▶ 动手试一试

如果你想让每个类型自己决定怎么构造,就把函数放进一个 trait:trait Reset { fn fresh() -> Self; }。在 trait 里,Self 表示“实现它的那个类型”。参见泛型和 trait。

在 main 或普通函数里写 Self { ... }。 let c = Self { count: 0 }; 也会报 E0411,只是标题稍有不同:cannot find struct, variant or union type `Self` in this scope。修复:写成 Counter { count: 0 },或者调用 Counter::new()。

方法里嵌套的辅助函数。 在 impl Counter 里,如果某个方法内部又定义了自己的 fn helper() -> Self,你会得到另一个错误码 E0401:can't use `Self` from outer item(不能使用外层项的 Self)。函数里面的函数不会继承外层的 impl。修复:在内层函数里写 Counter,或者改用闭包,或者把 helper 挪到 impl 里,作为一个独立的方法。

把 Self 和 self 搞混。 大写的 Self 是类型;小写的 self 是调用方法时的那个值。在方法外面用 self 是另一个错误,E0424。两者的修复方法相同:确认代码放在了正确的 impl 块里。

  • E0424:在方法外面用了 self(小写,表示值)。
  • E0425:找不到某个普通名字的值、函数,或者(从 Rust 1.93 起)类型。
  • E0412:“cannot find type”(找不到类型)的旧错误码,现在报为 E0425。
  • E0405:找不到某个 trait,通常是少了一行 use。
  • E0401:在嵌套函数里用了外层 impl 的 Self,见上文。
  • 所有编译错误:错误页面的完整列表。

小测验

1. 在哪里可以把 Self 当类型用?

2. 顶层的 fn make_user() -> Self 报了 E0411。通常怎么修?

3. 在 impl Counter 里,Self { count: 0 } 创建的是什么?

得分: 0 / 3