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 here2 | 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 here2 | 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 也负责找不到变量和函数的情况。下面讲的内容对这两个代码都一样适用。
这个错误是什么意思
Section titled “这个错误是什么意思”^^^^^^ 指向签名里的 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 为什么要这样做
Section titled “Rust 为什么要这样做”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 欧元修复 2:加上 rustc 建议的 use 行
Section titled “修复 2:加上 rustc 建议的 use 行”当 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 建议了好几个导入,先读一读再选:它们是同名的不同类型。
修复 3:声明泛型类型参数
Section titled “修复 3:声明泛型类型参数”当 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']));}输出:
100y结构体也一样:struct Wrapper { items: Vec<Item> } 会报错,除非你写成 struct Wrapper<Item>,或者定义一个真正的 Item 类型。如果漏掉 : PartialOrd,就会在 > 那里得到 E0369。参见泛型。
修复 4:从另一个模块导入类型
Section titled “修复 4:从另一个模块导入类型”当这个类型是你自己写的,但放在另一个模块里时,选这个方法。有一点常常让人意外:你文件里面的模块,同样看不到文件顶部定义的类型。每个模块都有自己的作用域:
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
- E0412 — Rust error codes index(英文):官方页面,注明了这个代码已不再输出,并保留了经典示例。
- E0425 — Rust error codes index(英文):如今的编译器给这个错误用的代码。
- 泛型数据类型 — Rust 程序设计语言:怎样声明
<T>,用的正是修复 3 里的largest函数。 - 使用 use 关键字将路径引入作用域 — Rust 程序设计语言:用
use、crate::和super::引入其他模块的类型。 std::prelude— 标准库文档(英文):不用导入就能直接使用的名字列表。