跳转到内容

Rust 错误 E0599:no method named X found(找不到方法)

E0599 的意思是:你调用了一个方法,但 Rust 在这个类型上找不到它。这个错误非常常见,原因通常就是那么几种:忘了导入某个 trait、没有先拆开 Option、把 Vec 当成了迭代器,或者只是拼错了。好消息是,rustc 的 help: 行经常会直接告诉你该怎么改。本页是 Rust 编译错误详解系列的一部分。

error[E0599]: no method named `write_all` found for struct `Stdout` in the current scope
--> src/main.rs:5:9
|
5 | out.write_all(b"Server started\n").unwrap();
| ^^^^^^^^^
|
= note: the method is available for `Stdout` here
|
= help: items from traits can only be used if the trait is in scope
help: trait `Write` which provides `write_all` is implemented but not in scope; perhaps you want to import it
|
1 + use std::io::Write;
|

第一行的意思是“在当前作用域里,结构体 Stdout 上找不到名为 write_all 的方法”。help 说明:trait 里的方法只有在这个 trait 位于作用域内时才能使用;提供 write_all 的 Write trait 已经实现了,只是没有引入,所以建议你导入它。

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

use std::io;
fn main() {
let mut out = io::stdout();
out.write_all(b"Server started\n").unwrap();
}

^^^^ 标出的是方法名 write_all,第一行则告诉你 Rust 是在哪个类型上找的:Stdout。所以这条信息说的是:“我在 Stdout 上找过了,没有一个叫 write_all、而且在这里允许你用的方法。”

第一行里的类型是整条信息里最有用的线索。如果它写的是 Option<T>、Result<T, E>、Vec<_>,或者一个类型参数 T,而你以为是别的类型,那你已经知道该用下面哪种修法了。

在 Rust 里,很多方法并不属于类型本身,而是来自某个 trait。write_all 来自 Write trait,Stdout 实现了它。但 trait 的方法只有在文件里引入了这个 trait(通常是用一行 use)时才能用。这就是 “items from traits can only be used if the trait is in scope” 的意思。

这条规则让代码更容易预测。两个 crate 可能各自通过不同的 trait,给同一个类型加上叫 write_all 的方法。如果所有 trait 都始终生效,你就分不清一次调用指的是哪一个。有了这条规则,只有你导入的 trait 才算数。

其他几种原因来自 Rust 严格的类型系统。Option<&str> 不是 &str,所以它没有 &str 的方法。泛型 T 可以是任何类型,所以 Rust 只允许你调用 T 的约束所保证的方法。

当 help 写着 “items from traits can only be used if the trait is in scope” 时,就用这个方法。加上 rustc 建议的那行 use。

use std::io::{self, Write};
fn main() {
let mut out = io::stdout();
out.write_all(b"Server started\n").unwrap();
}

输出:

Server started
▶ 动手试一试

(b"..." 是字节字符串,里面只能写 ASCII 字符,所以这里的文字保留英文。)

用 rand crate 时也会遇到同样的情况。没有导入的话,rng.random_range(1..=6) 会报错,rustc 会告诉你要引入哪个 trait:

help: trait `RngExt` which provides `random_range` is implemented but not in scope; perhaps you want to import it
|
1 + use rand::RngExt;

注意:在 rand 0.10 里,这个 trait 叫 RngExt。旧教程写的是 use rand::Rng,而在 rand 0.10 上这样导入,得到的正是同一个错误。照着 help: 行改,别照着旧教程改。

修复 2:先把值从 Option 或 Result 里取出来

Section titled “修复 2:先把值从 Option 或 Result 里取出来”

当错误里的类型是 Option<T> 或 Result<T, E> 时,就用这个方法。你要的值在里面,调用它的方法之前得先把它取出来。

error[E0599]: no method named `to_uppercase` found for enum `Option<T>` in the current scope
--> src/main.rs:4:39
|
4 | println!("First guest: {}", first.to_uppercase());
| ^^^^^^^^^^^^ method not found in `Option<&&str>`

first() 返回的是 Option,因为列表可能是空的。rustc 会建议你用 .expect(...),这样能用,但遇到 None 程序就会崩溃。通常把两种情况都处理掉更好:

fn main() {
let guests = vec!["ana", "bo", "chen"];
match guests.first() {
Some(name) => println!("First guest: {}", name.to_uppercase()),
None => println!("No guests yet"),
}
let shout = guests.last().map(|name| name.to_uppercase());
println!("Last guest: {:?}", shout);
}

输出:

First guest: ANA
Last guest: Some("CHEN")
▶ 动手试一试

.map() 会对里面的值运行你的代码,同时保留外面那层 Option。如果是 Result(比如 .parse() 的返回值),就用 match、.unwrap_or(...),或者在返回 Result 的函数里用 ?。详见 Option 和 Result。

当你直接在 Vec 上调用 .map()、.filter() 或其他迭代器方法时,就用这个方法。这时的错误看起来不太一样:

error[E0599]: the method `map` exists for struct `Vec<{integer}>`, but its trait bounds were not satisfied
--> src/main.rs:3:35
|
3 | let curved: Vec<i32> = scores.map(|s| s + 5).collect();
| ^^^
|
= note: the following trait bounds were not satisfied:
`Vec<{integer}>: Iterator`

意思是:Vec<{integer}> 上确实有 map 方法,但它的 trait 约束没有满足,具体是 Vec<{integer}>: Iterator 这一条。

对于 filter 等一些方法,提示会更友好:rustc 会说 Vec<i32> “is not an iterator”(不是迭代器),并加上 help: call `.into_iter()` first(先调用 .into_iter())。原因相同,修法也相同。

Vec<{integer}>: Iterator 之所以“not satisfied”(不满足),是因为 Vec 是一个集合,而不是迭代器。用 .iter()(借用元素)或 .into_iter()(拿走元素)向它要一个迭代器:

fn main() {
let scores = vec![70, 85, 92];
let curved: Vec<i32> = scores.iter().map(|s| s + 5).collect();
println!("{:?}", curved);
}

输出:

[75, 90, 97]
▶ 动手试一试

修复 4:给泛型 T 加上 trait 约束

Section titled “修复 4:给泛型 T 加上 trait 约束”

当错误说找不到方法的是 “for type parameter T”(类型参数 T)时,就用这个方法。在泛型函数里,Rust 只知道 T 的约束保证了什么。光秃秃的 T 什么都不保证,所以它没有任何方法。

error[E0599]: no method named `to_string` found for type parameter `T` in the current scope
--> src/main.rs:2:26
|
1 | fn describe<T>(item: T) -> String {
| - method `to_string` not found for this type parameter
2 | format!("[{}]", item.to_string())
| ^^^^^^^^^ method cannot be called on `T` due to unsatisfied trait bounds
|
help: the following trait defines an item `to_string`, perhaps you need to restrict type parameter `T` with it:
|
1 | fn describe<T: ToString>(item: T) -> String {
| ++++++++++

help 的意思是:ToString 这个 trait 定义了 to_string,也许你需要用它来约束类型参数 T。加上 rustc 建议的约束。这样一来,只有能转换成字符串的类型才会被接受:

fn describe<T: ToString>(item: T) -> String {
format!("[{}]", item.to_string())
}
fn main() {
println!("{}", describe(42));
println!("{}", describe("准备好了"));
}

输出:

[42]
[准备好了]
▶ 动手试一试
  • 拼写错误,或者用了别的语言里的方法名。 在 Vec 上写 tasks.push_back(...),会得到 help: you might have meant to use `push`(你可能想用 push);写 tasks.lenght(),会得到 help: there is a method `len` with a similar name(有一个名字相近的方法 len)。修复:用 rustc 建议的名字。
  • 调用你从没写过的 Type::new()。 Rust 不会白送构造函数:Account::new("Ana", 100) 会报 no associated function or constant named `new` found for struct `Account`。修复:加上 impl Account { fn new(owner: &str, balance: i64) -> Self { ... } },或者直接用 Account { owner: ..., balance: ... } 来创建。详见结构体。
  • 用点号调用关联函数。 如果 fn new() -> Self 没有 self 参数,acc.new() 会报 “this is an associated function, not a method”(这是关联函数,不是方法)。修复:在类型上调用它,Account::new()。

小测验

1. rustc 提示 "items from traits can only be used if the trait is in scope"。你应该怎么做?

2. names.first().to_uppercase() 报了 E0599,类型是 Option<&&str>。为什么?

3. 你在 Vec<i32> 上调用 scores.filter(...),rustc 说 "Vec<i32> is not an iterator"。该怎么改?

得分: 0 / 3