跳转到内容

Rust 错误 E0412:cannot find type in this scope(找不到类型)

E0412 的意思是:你写了一个类型名,可 Rust 在那个位置看不到它,比如函数签名 fn print_invoice(inv: &Invoce) 里的 Invoce。这个错误很常见,也很好修:要么是拼写错了,要么是少了一行 use,要么是用了泛型 T 却忘了声明 <T>。有一点可能先让你犯迷糊:从 Rust 1.93 起,编译器把这个错误显示为 E0425,信息还是同样的 cannot find type。本页两种情况都会讲。这个系列的其他错误,请看 Rust 编译错误详解。

Rust 1.93 及更新的版本会这样输出(当前的编译器是 1.97):

error[E0425]: cannot find type `Invoce` in this scope
--> src/main.rs:2:24
|
1 | struct Invoice { total: u32 }
| -------------- similarly named struct `Invoice` defined here
2 | fn print_invoice(inv: &Invoce) { // ❌ typo: Invoce
| ^^^^^^
|
help: a struct with a similar name exists
|
2 | fn print_invoice(inv: &Invoice) { // ❌ typo: Invoce
| +

同一个程序在 Rust 1.92 或更早的版本上是这样的。大多数博客文章和 Stack Overflow 回答都是用这些版本写的:

error[E0412]: cannot find type `Invoce` in this scope
--> src/main.rs:2:24
|
1 | struct Invoice { total: u32 }
| -------------- similarly named struct `Invoice` defined here
2 | fn print_invoice(inv: &Invoce) {
| ^^^^^^ help: a struct with a similar name exists: `Invoice`

两段输出都来自下面这个程序:

struct Invoice { total: u32 }
fn print_invoice(inv: &Invoce) { // ❌ typo: Invoce
println!("{}", inv.total);
}
fn main() {
print_invoice(&Invoice { total: 120 });
}

第一行的意思是“在这个作用域里找不到类型 Invoce”。help: 告诉你“有一个名字相近的结构体”,也就是 Invoice。

官方的 E0412 页面(英文)现在开头就写着“this error code is no longer emitted by the compiler”(编译器已不再输出这个错误代码)。问题本身并没有消失,只是换到了 E0425 名下,而 E0425 也负责找不到变量和函数的情况。下面讲的内容对这两个代码都一样适用。

^^^^^^ 指向签名里的 Invoce。要检查这个函数,Rust 必须知道 Invoce 是什么:它有多大、有哪些字段。Rust 把第 2 行能看到的所有类型都找了一遍(你自己的结构体和枚举、你的 use 行,还有 prelude),没找到叫这个名字的。

help: 这一行会告诉你属于哪种情况:

  • “a struct with a similar name exists”(有名字相近的结构体):拼写错误。+ 标出了少写的字母。
  • “consider importing this struct”(考虑导入这个结构体):这个类型在别的地方存在,你需要加一行 use。
  • “you might be missing a type parameter”(你可能少写了一个类型参数):你用了泛型 T,却没有声明 <T>。
  • 完全没有 help:这个类型还不存在。你还没写它,或者它在一个 Rust 不知道的文件里。

就像一份菜谱写着“加入 200 克 Invoce”。厨师知道面粉和糖是什么,却不知道“Invoce”是什么,所以还没开工就停下了。

Rust 在程序运行之前就会检查每个函数的签名。每个参数类型和返回类型都必须是一个真实存在、已知的类型,因为 Rust 要靠它来决定这个值需要多少内存、有哪些方法和字段。一个找不到的名字,Rust 不会去猜。

不用 use 就能在任何地方使用的类型名只有一小部分,那就是 prelude(预导入),里面有 String、Vec、Option、Result、Box 等少数几个。其他类型要么是你自己定义的,要么需要一行 use,要么写出完整路径,比如 std::num::ParseIntError。路径和 use 的用法见模块。

为什么错误代码变了?从 Rust 1.93 起,rustc 把找不到类型也归到 E0425 下面,和找不到变量或函数用同一个代码。信息里仍然写着 type,所以你总能看出缺的是哪一类名字。

修复 1:改正拼写,或者定义这个类型

Section titled “修复 1:改正拼写,或者定义这个类型”

当 rustc 说有名字相近的类型,或者根本没给 help 时,选这个方法。类型名区分大小写:invoice、Invoce 和 Invoice 是三个不同的名字。如果没有相近的名字,就先写出这个 struct 或 enum:

struct Invoice {
total: u32,
}
fn print_invoice(inv: &Invoice) {
println!("发票金额:{} 欧元", inv.total);
}
fn main() {
print_invoice(&Invoice { total: 120 });
}

输出:

发票金额:120 欧元
▶ 动手试一试

当 help 说 consider importing this struct(或者 enum、类型别名)时,选这个方法。标准库里不在 prelude 中的类型,比如 HashMap、ParseIntError 或 Ordering,都需要一行 use。下面这个函数返回一个解析错误:

error[E0425]: cannot find type `ParseIntError` in this scope
--> src/main.rs:1:40
|
1 | fn parse_age(text: &str) -> Result<u8, ParseIntError> {
| ^^^^^^^^^^^^^ not found in this scope
|
help: consider importing this struct
|
1 + use std::num::ParseIntError;
|

把建议的那一行复制到文件开头:

use std::num::ParseIntError;
fn parse_age(text: &str) -> Result<u8, ParseIntError> {
text.trim().parse()
}
fn main() {
println!("{:?}", parse_age(" 42 "));
println!("{:?}", parse_age("forty").is_err());
}

输出:

Ok(42)
true
▶ 动手试一试

如果你写的是 HashMap::new()(带 :: 的路径),而不是签名里的类型,那报的就是 E0433,用同样的 use 行就能修好。如果 rustc 建议了好几个导入,先读一读再选:它们是同名的不同类型。

当 help 说 you might be missing a type parameter 时,选这个方法。在 Rust 里,T 并不是什么特殊的关键字。它是你自己起的名字,必须在函数名后面用尖括号声明出来。没有 <T>,Rust 就会去找一个真正叫 T 的类型:

error[E0425]: cannot find type `T` in this scope
--> src/main.rs:1:21
|
1 | fn largest(items: &[T]) -> &T {
| ^ not found in this scope
|
help: you might be missing a type parameter
|
1 | fn largest<T>(items: &[T]) -> &T {
| +++

T 出现几次,这个错误就会报几次。加上 <T>,再加上函数需要的 trait。要用 > 比较元素,T 必须实现 PartialOrd:

fn largest<T: PartialOrd>(items: &[T]) -> &T {
let mut best = &items[0];
for item in items {
if item > best {
best = item;
}
}
best
}
fn main() {
println!("{}", largest(&[34, 50, 25, 100, 65]));
println!("{}", largest(&['y', 'm', 'a', 'q']));
}

输出:

100
y
▶ 动手试一试

结构体也一样:struct Wrapper { items: Vec<Item> } 会报错,除非你写成 struct Wrapper<Item>,或者定义一个真正的 Item 类型。如果漏掉 : PartialOrd,就会在 > 那里得到 E0369。参见泛型。

当这个类型是你自己写的,但放在另一个模块里时,选这个方法。有一点常常让人意外:你文件里面的模块,同样看不到文件顶部定义的类型。每个模块都有自己的作用域:

error[E0425]: cannot find type `Order` in this scope
--> src/main.rs:7:26
|
7 | pub fn print(order: &Order) -> String {
| ^^^^^ not found in this scope
|
help: consider importing this struct
|
7 + use crate::Order;
|

7 + 的意思是“在模块里面、第 7 行的位置加上这一行”。crate::Order 是从 crate 顶层开始的路径;super::Order(“从父模块”)也可以:

struct Order {
id: u32,
total: u32,
}
mod receipts {
use crate::Order;
pub fn print(order: &Order) -> String {
format!("订单 #{}:{} 欧元", order.id, order.total)
}
}
fn main() {
let order = Order { id: 7, total: 42 };
println!("{}", receipts::print(&order));
}

输出:

订单 #7:42 欧元
▶ 动手试一试

反过来也一样:定义在 mod shop 里的类型,要写成 pub struct Product,再加上 use crate::shop::Product;(或者写完整路径 shop::Product),main 才能使用它。你最常在 mod tests 里遇到这种情况,那里通常在开头写 use super::*;,把父模块的所有内容都导入进来。

类型来自一个你没有声明的文件。 有 src/models.rs 文件,却没写 mod models;,这时 fn save(user: models::User) 报的不是 E0412 或 E0425,而是 E0433:cannot find module or crate `models` in this scope(在这个作用域里找不到模块或 crate models)。修复:在 main.rs 开头加上 mod models;,并把结构体改成 pub。

在 impl 外面用 Self。 在顶层写 fn new_counter() -> Self 会得到另一个错误代码 E0411:cannot find type `Self` in this scope,标注是 `Self` is only available in impls, traits, and type definitions(Self 只能用在 impl、trait 和类型定义里)。修复:把函数挪进 impl Counter { ... },或者把 Self 换成 Counter。

要的是 trait,不是类型。 没有导入 Display 就写 impl Display for Point,会得到 E0405:cannot find trait `Display` in this scope,并附有 help: consider importing this trait 和 use std::fmt::Display;。道理一样,修法也一样:加上那行 use。参见 trait。

  • E0425:找不到值、函数,从 Rust 1.93 起也包括类型。这是你今天实际会看到的代码。
  • E0433:找不到路径的第一部分,比如 HashMap::new() 或 models::User。
  • E0432:无法解析的导入,也就是 use 行本身指向了不存在的东西。
  • E0603:类型找到了,但它不是 pub。
  • E0405(找不到 trait)和 E0411(在 impl 外使用 Self):上面提到的两个近亲。
  • 所有编译错误:错误页面的完整列表。

小测验

1. 你跟着的教程里显示的是 error[E0412]: cannot find type,可你的编译器输出的是 error[E0425]: cannot find type。为什么?

2. fn first(items: &[T]) -> &T 报了 cannot find type T。怎么修?

3. mod billing 里的一个函数用到了 Order,这个结构体定义在 main.rs 顶部。在模块里面加哪一行能修好?

得分: 0 / 3