コンテンツにスキップ

Rustのエラー E0599: no method named X found(メソッドが見つからない)

E0599 は、呼び出したメソッドを、Rust がその型の上で見つけられなかった、という意味のエラーです。とてもよく出会うエラーで、原因はたいてい次のどれかです。インポートしていないトレイト、中身を取り出していない 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;
|

1行目は「現在のスコープで、構造体 Stdout に write_all というメソッドが見つからない」という意味です。help は「トレイトのアイテムは、そのトレイトがスコープにあるときだけ使える」「write_all を提供する Write トレイトは実装されているがスコープにない。インポートしてはどうか」と教えてくれています。

このエラーが出るのは、次のプログラムです。

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

^^^^ の印が付いているのはメソッド名の write_all で、1行目には Rust がどの型を調べたかが書かれています。Stdout ですね。つまりこのメッセージは、「Stdout を調べたけれど、ここで使ってよい write_all というメソッドは見つからなかった」と言っています。

この1行目に出てくる型が、いちばん役に立つ手がかりです。思っていた型ではなく Option<T>、Result<T, E>、Vec<_>、あるいは型引数 T と書かれていたら、下のどの直し方が必要か、もうわかったようなものです。

Rust では、多くのメソッドは型そのものに属しているわけではありません。トレイト(trait)から来ています。write_all は Write トレイトのメソッドで、Stdout はこのトレイトを実装しています。ただし、トレイトのメソッドが使えるのは、そのトレイトがスコープに入っているファイルの中だけです。ふつうは use の行で持ち込みます。「items from traits can only be used if the trait is in scope」は、まさにこのことを言っています。

このルールのおかげで、コードの動きが予測しやすくなります。2つのクレートが、別々のトレイトを通して、同じ型に write_all という名前のメソッドを追加することもできます。もしすべてのトレイトがいつも有効だったら、その呼び出しがどちらを指すのかわかりません。このルールがあるので、数に入るのは自分でインポートしたトレイトだけです。

ほかの原因は、Rust の厳密な型から来ています。Option<&str> は &str ではないので、&str のメソッドは持っていません。ジェネリックな T はどんな型にでもなれるので、Rust は T の境界(bound)が約束しているメソッドしか呼ばせてくれません。

直し方1:トレイトをインポートする

Section titled “直し方1:トレイトをインポートする”

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
▶ 実際に試してみよう

(エラーになる例と比べやすいように、ここでは表示する文字列を英語のままにしています。)

rand クレートでも同じことが起きます。インポートしないと rng.random_range(1..=6) がエラーになり、rustc がどのトレイトを持ち込めばよいか教えてくれます。

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 では、このトレイトは 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>`

「列挙型 Option<T> に to_uppercase というメソッドが見つからない」「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 メソッドはあるが、トレイト境界を満たしていない」「Vec<{integer}>: Iterator が満たされていない」という意味です。filter などのメソッドでは、メッセージがもう少し親切です。rustc は Vec<i32> が「is not an iterator」(イテレータではない)と言い、help: call `.into_iter()` first(先に .into_iter() を呼んで)と付け加えます。原因も直し方も同じです。

Vec<{integer}>: Iterator が「満たされていない」のは、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 にトレイト境界を付ける

Section titled “直し方4:ジェネリックな T にトレイト境界を付ける”

エラーに「for type parameter T」(型引数 T に対して)メソッドが見つからない、と出たときは、これを選びます。ジェネリクスの関数の中では、Rust は T の境界が約束していることしか知りません。何も付いていない T は何も約束していないので、メソッドを1つも持っていません。

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 は「to_string を定義しているのは次のトレイトなので、型引数 T をそれで制限する必要があるかもしれない」と提案しています。rustc が提案する境界を付けましょう。これで、文字列に変換できる型だけを受け取るようになります。

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

実行結果:

[42]
[ready]
▶ 実際に試してみよう
  • つづりの間違いや、ほかの言語のメソッド名。 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`(構造体 Account に new という関連関数や定数がない)というエラーになります。直し方: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() が Option<&&str> について E0599 になりました。なぜですか?

3. Vec<i32> に scores.filter(...) を呼んだら、rustc が「Vec<i32> is not an iterator」と言いました。直し方はどれですか?

スコア: 0 / 3