Rustのエラー E0034: multiple applicable items in scope(同じ名前のメソッドが複数)
E0034 は、自分の型に同じ名前のメソッドが2つ(2つのトレイトから1つずつ)あるのに、book.describe() のような短い書き方で呼んだ、という意味のエラーです。Rust には両方が見えていて、勝手にどちらかを選んではくれません。型がおかしいわけではありません。どのトレイトのつもりなのかを書けばいいだけです。たいていは1行で済み、その1行も rustc が書いてくれます。このシリーズのほかのエラーは、Rustのコンパイルエラー解説を見てください。
エラーメッセージ
Section titled “エラーメッセージ”error[E0034]: multiple applicable items in scope --> src/main.rs:28:25 |28 | println!("{}", book.describe()); // ❌ Summary's or Label's? | ^^^^^^^^ multiple `describe` found |note: candidate #1 is defined in an impl of the trait `Label` for the type `Book` --> src/main.rs:21:5 |21 | fn describe(&self) -> String { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^note: candidate #2 is defined in an impl of the trait `Summary` for the type `Book` --> src/main.rs:15:5 |15 | fn describe(&self) -> String { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^help: disambiguate the method for candidate #1 |28 - println!("{}", book.describe()); // ❌ Summary's or Label's?28 + println!("{}", Label::describe(&book)); // ❌ Summary's or Label's? |help: disambiguate the method for candidate #2 |28 - println!("{}", book.describe()); // ❌ Summary's or Label's?28 + println!("{}", Summary::describe(&book)); // ❌ Summary's or Label's? |「multiple applicable items in scope」は「スコープの中に、当てはまるアイテムが複数ある」という意味です。
このエラーが出るのは、次のプログラムです。
trait Summary { fn describe(&self) -> String;}
trait Label { fn describe(&self) -> String;}
struct Book { title: String, pages: u32,}
impl Summary for Book { fn describe(&self) -> String { format!("{} ({} pages)", self.title, self.pages) }}
impl Label for Book { fn describe(&self) -> String { format!("BOOK: {}", self.title.to_uppercase()) }}
fn main() { let book = Book { title: String::from("Rust for Crabs"), pages: 320 }; println!("{}", book.describe()); // ❌ Summary's or Label's?}コメントは「Summary のもの? それとも Label のもの?」という意味です(エラーになる例なので、コードはそのままにしています)。
エラーの意味
Section titled “エラーの意味”メッセージを上から順に読んでいきましょう。
describeの下の^^^^^^^^:「multipledescribefound」(describeが複数見つかった)。Rust はBookにdescribeというメソッドを探して、2つ以上見つけました。- 2つの
note:の行は候補の一覧です。1つはimpl Label for Book(21行目)から、もう1つはimpl Summary for Book(15行目)から来ています。どちらも本物で、どちらも動き、返す文字列は違います。 - 2つの
help:の行は、それぞれを選んだときの直し方です(disambiguate は「あいまいさをなくす」という意味です)。Summary::describe(&book)は「Summaryから来たdescribeを、&bookをselfにして呼ぶ」という意味です。
同じオフィスに「佐藤さん」が2人いるようなものです。「佐藤さん!」と呼んでも、どちらが振り向けばいいかわかりません。「経理の佐藤さん」と言えば伝わります。Summary::describe が「経理の佐藤さん」です。
Rust がこうする理由
Section titled “Rust がこうする理由”book.describe() のようなメソッド呼び出しは、近道の書き方です。Rust は book の型を見て、その型自身のメソッドと、スコープの中にあって Book に実装されているすべてのトレイトのメソッドを探します。ふつうは、ちょうど1つが見つかります。ここでは2つ見つかり、しかもやることが違います。1つは要約を、もう1つは本棚用のラベルを返します。
「先に定義されたトレイトを優先する」のような隠れたルールで、Rust がどちらかを選ぶこともできたでしょう。でもそうすると、トレイトを1つ追加したり、use の行の順番を入れ替えたりしただけで、プログラムの表示が黙って変わってしまいます。Rust は当てずっぽうを拒み、誰が読んでもわかるように、コードの中で一度はっきり書いてほしいと頼んでいるのです。
例外が1つあります。型自身の impl Book ブロックにあるメソッドは、同じ名前のトレイトのメソッドよりいつも優先されるので、その場合は E0034 になりません。詳しくはトレイトを実装すると完全修飾構文:名前がぶつかったときを見てください。
直し方1:トレイトを通して呼ぶ:Trait::method(&x)
Section titled “直し方1:トレイトを通して呼ぶ:Trait::method(&x)”両方のメソッドが必要で、そのたびにどちらかを書けばいいときは、これを選びます。rustc が提案しているのも、まさにこの方法です。値を最初の引数として渡します。そのとき、メソッドの &self に合わせて同じ & を付けます。
trait Summary { fn describe(&self) -> String;}
trait Label { fn describe(&self) -> String;}
struct Book { title: String, pages: u32,}
impl Summary for Book { fn describe(&self) -> String { format!("{} ({} pages)", self.title, self.pages) }}
impl Label for Book { fn describe(&self) -> String { format!("BOOK: {}", self.title.to_uppercase()) }}
fn main() { let book = Book { title: String::from("Rust for Crabs"), pages: 320 }; println!("{}", Summary::describe(&book)); println!("{}", Label::describe(&book));}実行結果:
Rust for Crabs (320 pages)BOOK: RUST FOR CRABS上のエラーになる例と見比べやすいように、表示する文字列は英語のままにしています。
メソッドが &mut self を受け取るなら &mut book を、self を受け取るなら book を渡します(ドットで呼んだときと同じように、値はムーブされます)。
直し方2:完全修飾構文:<Type as Trait>::method()
Section titled “直し方2:完全修飾構文:<Type as Trait>::method()”ぶつかっている関数に self がないとき(コンストラクタや fn category() など)は、これを選びます。この場合、Rust が手がかりにできる値がないので、Trait::category() だけでは足りません。型の名前も書く必要があります。<Book as Summary>::category() は「Book が Summary を実装しているから持っている category」と読みます。これはメソッドにも使えます。
trait Summary { fn category() -> String; fn describe(&self) -> String;}
trait Label { fn category() -> String;}
struct Book { title: String,}
impl Summary for Book { fn category() -> String { String::from("ノンフィクション") } fn describe(&self) -> String { format!("『{}』という本", self.title) }}
impl Label for Book { fn category() -> String { String::from("B棚") }}
fn main() { let book = Book { title: String::from("Rust for Crabs") }; println!("{}", <Book as Summary>::category()); println!("{}", <Book as Label>::category()); println!("{}", <Book as Summary>::describe(&book));}実行結果:
ノンフィクションB棚『Rust for Crabs』という本ここで Book::category() と書いても E0034 になり、use fully-qualified syntax to disambiguate(あいまいさをなくすには完全修飾構文を使ってください)というヒントが出ます。Summary::category() とだけ書くと、別のエラー E0790 になります。トレイトには、どの型のことなのかがまだわからないからです。
直し方3:どちらかのメソッドの名前を変える
Section titled “直し方3:どちらかのメソッドの名前を変える”両方のトレイトが自分のもので、名前がたまたま同じになっただけなら、これを選びます。違うことをする2つのメソッドには、違う名前が似合います。そうすれば、短い book.describe() がまた使えるようになります。多くの場合、これがいちばんすっきりした直し方です。
trait Summary { fn describe(&self) -> String;}
trait Label { fn label_text(&self) -> String;}
struct Book { title: String, pages: u32,}
impl Summary for Book { fn describe(&self) -> String { format!("{} ({} pages)", self.title, self.pages) }}
impl Label for Book { fn label_text(&self) -> String { format!("BOOK: {}", self.title.to_uppercase()) }}
fn main() { let book = Book { title: String::from("Rust for Crabs"), pages: 320 }; println!("{}", book.describe()); println!("{}", book.label_text());}実行結果:
Rust for Crabs (320 pages)BOOK: RUST FOR CRABSもちろん、ほかのクレートのトレイトのメソッドは名前を変えられません。その場合は、直し方1か直し方4を使いましょう。
直し方4:必要なトレイトだけをインポートする
Section titled “直し方4:必要なトレイトだけをインポートする”トレイトが別々のモジュールやクレートにあるときは、これを選びます。トレイトのメソッドをドットで呼べるのは、そのトレイトがスコープの中にあるときだけです。だから、コードのある部分で片方しか必要ないなら、そこではその1つだけをインポートしましょう。use は関数の中にも書けて、そうするとその関数の中だけで有効になります。
mod report { pub trait Summary { fn describe(&self) -> String; }}
mod shop { pub trait Label { fn describe(&self) -> String; }}
struct Book { title: String,}
impl report::Summary for Book { fn describe(&self) -> String { format!("『{}』という本", self.title) }}
impl shop::Label for Book { fn describe(&self) -> String { format!("BOOK: {}", self.title.to_uppercase()) }}
fn print_label(book: &Book) { use shop::Label; // ここでスコープにあるのは Label だけ println!("{}", book.describe());}
fn main() { use report::Summary; // ここでスコープにあるのは Summary だけ let book = Book { title: String::from("Rust for Crabs") }; println!("{}", book.describe()); print_label(&book);}実行結果:
『Rust for Crabs』という本BOOK: RUST FOR CRABSimpl ブロックでは report::Summary と shop::Label を省略せずに書いていることに注目してください。トレイトを実装するのに use はいりません。use が必要なのは、ドットで呼ぶときだけです。use について詳しくはモジュールを見てください。
よく出会う場面
Section titled “よく出会う場面”自分で作った to_string()。 Display を実装している型は、標準ライブラリの ToString トレイトから、もう to_string() をもらっています。そこに to_string メソッドを持つ自分のトレイトを足すと、point.to_string() が E0034 になり、candidate #2 is defined in an impl of the trait `ToString` for the type `T` (候補2はトレイト ToString の型 T への impl で定義されている)と表示されます。直し方:自分のほうの名前を変える(pretty()、to_label() など)か、Pretty::to_string(&point) と呼びます。
2つの prelude::* のインポート。 ライブラリはよく、自分のトレイトをまとめて取り込めるように use some_crate::prelude::*; を用意しています。同じ型に draw() や render() メソッドを追加するプレリュードを2つインポートすると、ドットでの呼び出しがあいまいになります。直し方:片方の * を、実際に使うアイテムだけのインポートに置き換えるか、Render::draw(&data) と呼びます。
トレイト境界が2つあるジェネリックな関数。 fn show<T: Summary + Label>(item: &T) の中では、item.describe() が E0034 になります。ここでは rustc の提案に気をつけてください。Label::describe(&item) を提案してきますが、item はすでに参照なので、そう書くと E0277(the trait bound `&T: Label` is not satisfied、&T が Label を実装していない)になります。余分な & を付けずに Label::describe(item) と書きましょう。詳しくはジェネリクスを見てください。
関連するエラー
Section titled “関連するエラー”- E0790:
Default::default()やSummary::category()のように、selfのないトレイトの関数を、型を指定せずに呼んだ。 - E0599:逆の問題。その名前のメソッドが1つも見つからない。トレイトをインポートしていないことがよくあります。
- E0277:トレイト境界を満たしていない。たとえば
Trait::method(&x)に余分な&を付けたとき。 - E0428:同じ名前が複数回定義されている。2つのトレイトのメソッドではなく、1つのモジュールの中に同じ名前のアイテムが2つある場合です。
- コンパイルエラーの一覧:エラー解説ページの全リストです。
理解度チェック
Section titled “理解度チェック”理解度チェック
1. Book は Summary と Label を実装していて、どちらにも fn describe(&self) があります。Label のほうを呼ぶのはどの行ですか?
2. どちらのトレイトにも、self のない fn category() -> String があります。動くのはどの呼び出しですか?
3. Book には自分の impl Book { fn describe(&self) ... } があり、describe を持つ Summary も実装しています。book.describe() はどうなりますか?
スコア: 0 / 3
さらに詳しく
Section titled “さらに詳しく”- E0034 — Rust error codes index(英語):このエラーの公式の説明です。2通りの完全修飾の書き方が載っています。
- 高度なトレイト — The Rust Programming Language 日本語版:完全修飾構文(フルパス記法)の節があり、
Pilot、Wizard、Humanの例が載っています。 - Disambiguating overlapping traits — Rust By Example 日本語版:同じ名前のメソッドを持つ2つのトレイトの、短い実行できる例です。
- トレイト:共通の振る舞いを定義する — The Rust Programming Language 日本語版:トレイトの定義と実装、そしてメソッドを使うにはトレイトがスコープの中にある必要がある理由を説明している章です。
- Method call expressions — The Rust Reference(英語):
x.method()の裏にあるメソッドを Rust が探すときの正確なルールです。