跳转到内容

Rust 错误 E0432:unresolved import(无法解析的导入)

E0432 的意思是:你的某一行 use 指向了 Rust 找不到的东西。可能是 crate 还没写进 Cargo.toml,可能是路径里有个小拼写错误,也可能是文件明明存在,Rust 却不知道它是一个模块。看起来有点吓人,但几乎总是改一行就好,而且编译器通常会告诉你是哪一行。本页属于 Rust 编译错误详解 系列。

error[E0432]: unresolved import `rand`
--> src/main.rs:1:5
|
1 | use rand::random_range;
| ^^^^ use of unresolved module or unlinked crate `rand`
|
= help: if you wanted to use a crate named `rand`, use `cargo add rand` to add it to your `Cargo.toml`

下面这段程序会产生这个错误。它放在一个用 cargo new 新建的项目里,[dependencies] 下面什么都没有:

use rand::random_range;
fn main() {
let roll = random_range(1..=6);
println!("You rolled {roll}");
}

^^^^ 下面的文字会随原因变化。下面每一种都是 E0432,各自对应后面不同的修复方法:

标注上写的是 通常哪里出了问题 修复
use of unresolved module or unlinked crate Cargo.toml 里没有这个 crate,或者文件没有对应的 mod 行 修复 1 或修复 3
could not find `collection` in `std` 路径中间有拼写错误 修复 2
no `Hashmap` in `collections` 最后一个名字拼错了(或大小写不对) 修复 2
could not find `inventory` in the crate root 用 crate:: 路径指向了一个没有声明的模块 修复 3
no external crate 路径的起点选错了模块 修复 4

use 行是一个快捷方式。use rand::random_range; 的意思是:“从现在起,random_range 指的就是 rand::random_range 那个东西。”在建立这个快捷方式之前,Rust 得沿着路径一段一段地走:先找到 rand,再在它里面找 random_range。

^^^^ 标出的是第一个失败的那一段。这里是 rand,也就是最前面那一段,所以 Rust 根本还没去找 random_range。help: 那一行猜测了原因:rand 是 crates.io 上的一个 crate,而这个项目还没有添加它。

这就像你告诉出租车司机去“春田镇海港街 12 号”,可镇名写错了,司机连从哪条街找起都不知道。

Rust 在编译时就解析每一条路径,而不是等到程序运行时。它不会“以防万一”提前加载任何东西。你的代码只能看到三类东西:Cargo.toml 里列出的 crate(再加上 std)、你用 mod 声明的模块,以及它们里面的项。如果某一行 use 提到的东西不在这个范围内,Rust 会立刻停下来,而不是去猜。

这其实是好事。在 Python 或 JavaScript 里,拼错的导入往往要等到那一行真正运行时才会失败,说不定是在生产环境里。在 Rust 里,它连编译都过不了。这也意味着,你从没添加过的 crate 永远不可能偷偷混进你的程序。

从 2018 edition 开始,use 路径从下面几个地方之一开始:某个 crate 的名字(std、rand)、crate(你自己 crate 的根)、self(当前模块)、super(父模块),或者当前模块里声明的某个模块。模块 一课会详细讲这些,Cargo 与 crate 则讲解依赖。

如果路径的第一段是 crates.io 上的 crate,比如 rand、serde 或 regex,并且 help: 那一行提到了 cargo add,就选这个办法。这是最常见的原因,从博客文章里复制代码时尤其如此。在包含 Cargo.toml 的文件夹里运行:

Terminal window
cargo add rand

Cargo 会找到最新版本,并帮你在 Cargo.toml 里加上一行:

[dependencies]
rand = "0.10.3"

现在同样的代码就能编译了。这个版本打印的是一个检查结果,而不是点数本身,所以每次输出都一样:

use rand::random_range;
fn main() {
let roll = random_range(1..=6);
println!("点数在 1 到 6 之间:{}", (1..=6).contains(&roll));
}

输出:

点数在 1 到 6 之间:true
▶ 动手试一试

这里有两个地方容易踩坑。第一,名字里带短横线的 crate,在代码里要写成下划线:你运行的是 cargo add tracing-subscriber,写的却是 use tracing_subscriber::...。第二,即使这个 crate 是你工作空间的依赖,它仍然必须列在使用它的那个包的 Cargo.toml 里。

如果标注写的是 could not find `X` in `Y` 或 no `X` in `Y`,就选这个办法。路径中间或末尾的某个名字拼错了。一个经典的例子:

error[E0432]: unresolved import `std::collection`
--> src/main.rs:1:10
|
1 | use std::collection::HashMap;
| ^^^^^^^^^^ could not find `collection` in `std`

这个模块叫 collections,末尾有个 s。名字还区分大小写:use std::collections::Hashmap; 会报 no `Hashmap` in `collections`,rustc 还会加上 help: a similar name exists in the module(模块里有一个相似的名字),并给出正确的拼写。改好的导入:

use std::collections::HashMap;
fn main() {
let mut stock: HashMap<&str, u32> = HashMap::new();
stock.insert("apples", 12);
println!("{:?}", stock.get("apples"));
}

输出:

Some(12)
▶ 动手试一试

如果不确定准确的路径,就去 标准库文档(英文)里搜一下。每个项的页面顶部都写着它的完整路径。关于这个类型的更多内容,见 HashMap。

如果你新建了一个文件,比如 src/inventory.rs,而 use inventory::... 找不到它,就选这个办法。在 Rust 里,文件在被父文件声明之前不算模块。你的项目是这样的:

src/
├── main.rs use inventory::count_items; ← 没有 `mod inventory;`
└── inventory.rs pub fn count_items() -> u32 { 42 }

rustc 会明确告诉你缺了什么:

error[E0432]: unresolved import `inventory`
--> src/main.rs:1:5
|
1 | use inventory::count_items;
| ^^^^^^^^^ use of unresolved module or unlinked crate `inventory`
|
help: to make use of source file src/inventory.rs, use `mod inventory` in this file to declare the module
|
1 + mod inventory;
|

在 src/main.rs 的顶部加上 mod inventory;(如果这个模块属于你的库,就加在 src/lib.rs 里)。改写成 use crate::inventory::count_items; 没有用,它会报 could not find `inventory` in the crate root,因为模块仍然没有声明。

Playground 里所有代码都在一个文件中,所以这里用 mod inventory { ... } 把模块直接写在文件里。它和单独的文件效果完全一样:

mod inventory {
pub fn count_items() -> u32 {
42
}
}
use inventory::count_items;
fn main() {
println!("库存数量:{}", count_items());
}

输出:

库存数量:42
▶ 动手试一试

每个模块只在它的父模块里声明一次。其他文件再通过 use crate::inventory::... 访问它。不要在每个用到它的文件里都加 mod inventory;。

修复 4:让路径从正确的地方开始

Section titled “修复 4:让路径从正确的地方开始”

如果导入写在某个模块里,而要用的项在你自己 crate 的另一个模块里,就选这个办法。这里 receipt 在 shop 里面,想用 shop::store_name:

mod shop {
pub fn store_name() -> &'static str {
"Ferris Fruits"
}
pub mod receipt {
use store_name; // ❌ looks in the wrong place
pub fn header() -> String {
format!("== {} ==", store_name())
}
}
}
error[E0432]: unresolved import `store_name`
--> src/main.rs:7:13
|
7 | use store_name; // ❌ looks in the wrong place
| ^^^^^^^^^^ no external crate `store_name`

(注释保留英文,因为它出现在错误信息里,意思是“找错了地方”。)

use 路径开头的一个单独名字,意思是“一个 crate,或者就在当前模块里声明的东西”。store_name 两者都不是,所以 Rust 去找了一个叫这个名字的 crate。你得告诉它从哪里开始找:

前缀 起点 示例
crate:: 你的 crate 的根(main.rs 或 lib.rs) use crate::shop::prices::price_of;
super:: 父模块 use super::store_name;
self:: 当前模块 use self::lines::line;

三种写法放在一个程序里:

mod shop {
pub fn store_name() -> &'static str {
"Ferris Fruits"
}
pub mod prices {
pub fn price_of(item: &str) -> u32 {
if item == "apple" { 3 } else { 5 }
}
}
pub mod receipt {
use super::store_name; // 父模块:shop
use crate::shop::prices::price_of; // 从 crate 根开始
use self::layout::line; // 当前模块的子模块
mod layout {
pub fn line(item: &str, price: u32) -> String {
format!("{item}: {price}")
}
}
pub fn print(item: &str) {
println!("== {} ==", store_name());
println!("{}", line(item, price_of(item)));
}
}
}
fn main() {
shop::receipt::print("apple");
}

输出:

== Ferris Fruits ==
apple: 3
▶ 动手试一试

这里写 use layout::line; 也行,因为 layout 就声明在当前模块里;self:: 只是把这一点写明白。拿不准的时候,用 crate:: 加完整路径,在 crate 里任何地方都管用,rustc 也经常这样建议:先是 help: a similar path exists(存在一个相似的路径),后面跟着 crate:: 版本。

为旧版 crate 写的代码。 很多教程用的是 rand 0.8。换成 rand 0.10 就会得到:

error[E0432]: unresolved import `rand::thread_rng`
--> src/main.rs:1:5
|
1 | use rand::thread_rng;
| ^^^^^^^^^^^^^^^^ no `thread_rng` in the root

修复:在 docs.rs 上查看你 Cargo.toml 里那个版本的 crate 文档。这里的新名字是 rand::rng()。见 随机数。

同时有 main.rs 和 lib.rs 的包。 它们是两个独立的 crate。在 main.rs 里,crate:: 指的是这个二进制 crate,所以即使 lib.rs 里有 pub mod inventory;,use crate::inventory::count_items; 也会报 could not find `inventory` in the crate root。修复:用包名,use shop_app::inventory::count_items;(短横线要换成下划线)。

项存在,但是私有的。 这是另一个错误 E0603,不是 E0432:

error[E0603]: function `count_items` is private
--> src/main.rs:7:16
|
7 | use inventory::count_items;
| ^^^^^^^^^^^ private function

修复:在 fn 前面加上 pub(路径上经过的每个 mod 前面也要加,因为私有模块会报 module `prices` is private)。见 模块。

  • E0433:代码里的某条路径(比如 HashMap::new() 或 rand::rng())无法解析。原因相同,只是不在 use 行上。
  • E0425:找不到某个值或函数,常常加上同样的 use 行就能解决。
  • E0603:导入找到了,但它是私有的。加上 pub。
  • E0463:can't find crate(找不到 crate),crate 不在 Cargo.toml 里时写 extern crate rand; 就会报这个错。
  • 所有编译错误:全部错误页面的列表。

小测验

1. 你把 use serde::Serialize; 复制进一个新项目,得到 E0432,说 crate serde 无法解析或没有链接。怎么修复?

2. 你新建了 src/billing.rs,但 main.rs 里的 use billing::total; 报错了。缺了什么?

3. 在 mod shop { mod receipt { ... } } 里,receipt 怎样导入直接定义在 shop 里的函数?

得分: 0 / 3