Rustのエラー E0592: duplicate definitions with name x(メソッドの重複定義)
E0592 は、1つの型に、同じ名前のメソッド、関連関数、または定数が impl ブロックの中で2つ書かれている、という意味のエラーです。1つの型に impl ブロックはいくつ書いてもかまいませんが、それらはすべて1つの名前のリストを共有しています。だから、それぞれの名前は1回しか使えません。たいていはコピー&ペースト、マージ、または別のファイルにある2つ目の impl ブロックが原因です。直し方は、1つだけ残すか、もう片方に新しい名前を付けることです。このシリーズのほかのエラーは、Rustのコンパイルエラー解説を見てください。
エラーメッセージ
Section titled “エラーメッセージ”error[E0592]: duplicate definitions with name `reset` --> src/main.rs:10:5 |10 | fn reset(&mut self) { | ^^^^^^^^^^^^^^^^^^^ duplicate definitions for `reset`...16 | fn reset(&mut self) { // ❌ `reset` is already defined above | ------------------- other definition for `reset`「duplicate definitions with name reset」は「reset という名前の定義が重複している」という意味です。
このエラーが出るのは、次のプログラムです。
struct Counter { count: u32,}
impl Counter { fn new() -> Self { Counter { count: 0 } }
fn reset(&mut self) { self.count = 0; }}
impl Counter { fn reset(&mut self) { // ❌ `reset` is already defined above self.count = 0; }}
fn main() { let mut c = Counter::new(); c.reset(); println!("{}", c.count);}コメントは「reset は上ですでに定義されている」という意味です(エラーになる例なので、コードはそのままにしています)。
rustc は、c.reset() の呼び出しのところで E0034「multiple applicable items in scope」(スコープ内に使えるアイテムが複数ある)も報告します。2つの reset のどちらのことなのか、わからないからです。これはあとから付いてくるエラーなので、E0592 を直せば E0034 も消えます。
エラーの意味
Section titled “エラーの意味”見出しの行には、2回定義されている名前(reset)が出ています。^^^ が一方の定義を示し、--- の行が「other definition」(もう1つの定義)を示しています。rustc は両方の行番号を表示してくれるので、すぐにそこへ飛べます。2つが離れているときに間に出る ... は、「何行か省略しています」という意味にすぎません。
2つの reset が別々の impl Counter ブロックにあっても、クレートの別々のファイルにあっても、関係ありません。Rust にとっては、どれも同じ型のメソッドです。だから Counter::reset は、ちょうど1つの関数を指さなければなりません。関連定数にも同じルールがあります。1つの型に const MAX: u32 が2つあれば、それも E0592 です。
スマホの連絡先に「お母さん」という登録が2つあるようなものです。「お母さんに電話して」と言われたら、どちらの番号にかければいいのでしょうか。
Rust がこうする理由
Section titled “Rust がこうする理由”メソッドをいくつかの impl ブロックに分けるのは許されていて、便利なこともよくあります。コンストラクタ用のブロック、ゲッター用のブロック、#[cfg(test)] の中のブロック、といった具合です。詳しくはメソッドで振る舞いを追加するを見てください。でも、c.reset() や Counter::reset(&mut c) と呼び出したとき、Rust は関数を1つ見つけなければなりません。もし2つあったら、どちらが動くかは、呼び出しを読んでもわからない細かい事情で決まってしまいます。
だから Rust は、呼び出しのところだけでなく、定義のところで断ります。これで本物のバグを防げます。コピーしたメソッドだと、片方のコピーで問題を直しても、もう片方のコピーが動き続けているかもしれません。Rust は、直すべきバージョンがいつも1つだけになるようにしてくれるのです。
直し方1:重複を消して、定義を1つだけ残す
Section titled “直し方1:重複を消して、定義を1つだけ残す”たいていはこれを選びます。2つのコピーが同じことをしているなら(コピー&ペースト、うまくいかなかったマージ、移動したのに元を消し忘れたメソッドなど)、片方を消します。消す前に、2つの中身を見比べましょう。片方にだけ修正が入っているなら、そちらを残します。impl ブロックを1つにまとめておくと、この先、重複にも気づきやすくなります。
struct Counter { count: u32,}
impl Counter { fn new() -> Self { Counter { count: 0 } }
fn increment(&mut self) { self.count += 1; }
fn reset(&mut self) { // ✅ `Counter` の `reset` は1つだけ self.count = 0; }}
fn main() { let mut c = Counter::new(); c.increment(); c.increment(); println!("リセット前: {}", c.count); c.reset(); println!("リセット後: {}", c.count);}実行結果:
リセット前: 2リセット後: 02つ目の定義が見つからないときは、プロジェクト全体を検索しましょう。grep -rn "fn reset" src/ です。impl Counter ブロックは、クレートのどのモジュールにでも置けるからです。
直し方2:片方の名前を変える
Section titled “直し方2:片方の名前を変える”2つの定義が違うことをしているなら、これを選びます。Rust にはオーバーロードがありません。reset() と reset(start: u32) のように、名前が同じで引数が違うメソッドを2つ持つことはできず、これも E0592 になります。それぞれに、何をするのかがわかる名前を付けましょう。
struct Counter { count: u32,}
impl Counter { fn new() -> Self { Counter { count: 0 } }
fn reset(&mut self) { self.count = 0; }}
impl Counter { fn reset_to(&mut self, start: u32) { // ✅ 違う動きには違う名前を self.count = start; }}
fn main() { let mut c = Counter::new(); c.reset_to(10); println!("reset_to(10) のあと: {}", c.count); c.reset(); println!("reset() のあと: {}", c.count);}実行結果:
reset_to(10) のあと: 10reset() のあと: 0Rust でよく使われる名前の付け方が参考になります。new と with_capacity、from_str と from_bytes、get と get_mut などです。いろいろな引数の型に、どうしても1つの名前を使いたいなら、fn reset_to(&mut self, start: impl Into<u32>) のように、ジェネリックな引数かトレイトを使います。詳しくはジェネリクスを見てください。
直し方3:ジェネリックな impl ブロックが重ならないようにする
Section titled “直し方3:ジェネリックな impl ブロックが重ならないようにする”2つの定義の片方が、impl<T> Wrapper<T> のようなジェネリックなブロックにあるときは、これを選びます。このブロックは Wrapper<i32> もカバーしているので、同じ名前のメソッドを持つ2つ目の impl Wrapper<i32> は重複になります。
error[E0592]: duplicate definitions with name `describe` --> src/main.rs:4:5 | 4 | fn describe(&self) -> String { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ duplicate definitions for `describe`...10 | fn describe(&self) -> String { | ---------------------------- other definition for `describe`このエラーが出るプログラムです。
struct Wrapper<T>(T);
impl<T> Wrapper<T> { fn describe(&self) -> String { String::from("something") }}
impl Wrapper<i32> { fn describe(&self) -> String { format!("the number {}", self.0) }}
fn main() { println!("{}", Wrapper(5).describe());}Rust では、特定の型向けの impl でジェネリックな impl を上書きすることはできません(この機能は特殊化(specialization)と呼ばれ、まだ安定版にはありません)。良い抜け道は2つあります。特定の型向けの impl ブロックだけを書いて重ならないようにするか、振る舞いをトレイトに入れて、型ごとに1回ずつ実装するかです。
struct Wrapper<T>(T);
// 方法A:特定の型向けの impl ブロックは重ならないimpl Wrapper<i32> { fn describe(&self) -> String { format!("数 {}", self.0) }}
impl Wrapper<&str> { fn describe(&self) -> String { format!("テキスト「{}」", self.0) }}
// 方法B:トレイトを、型ごとに1回ずつ実装するtrait Label { fn label(&self) -> String;}
impl Label for i32 { fn label(&self) -> String { format!("整数 {}", self) }}
impl Label for bool { fn label(&self) -> String { format!("フラグ {}", self) }}
impl<T: Label> Wrapper<T> { fn label(&self) -> String { self.0.label() // ジェネリックなメソッドは1つ。型ごとの部分はトレイトにある }}
fn main() { println!("{}", Wrapper(5).describe()); println!("{}", Wrapper("hi").describe()); println!("{}", Wrapper(7).label()); println!("{}", Wrapper(true).label());}実行結果:
数 5テキスト「hi」整数 7フラグ true長い目で見ると、たいていはトレイト版のほうが良い設計です。新しい型に対応したくなったら、impl Label for ... を1つ足すだけで済みます。詳しくはトレイトを見てください。
よく出会う場面
Section titled “よく出会う場面”2つ目の impl ブロックに書いたテスト用のヘルパー。 テスト用の値を作るために、#[cfg(test)] mod tests の中に impl Counter { fn new() -> Self { ... } } を書いたけれど、Counter::new はすでにあった、という場合です。テスト用のブロックも、やはり impl Counter です。直し方:fn for_test() のような名前にするか、本物の new を使います。詳しくはテストを見てください。
マージやリファクタリングのあと。 2つのブランチがどちらも fn len(&self) を追加した、あるいはメソッドを新しいファイルに移したのに古いコピーが残っていた、という場合です。rustc が両方の行番号を見せてくれるので、良いほうを残しましょう。
同じ名前のトレイトのメソッドなら問題ない。 impl Counter { fn reset(&mut self) } と impl Resettable for Counter { fn reset(&mut self) } を一緒に書いても、E0592 にはなりません。片方は型そのもののメソッドで、もう片方はトレイトのものだからです。c.reset() は型そのもののほうを呼びます。同じ名前のメソッドを持つトレイトが2つある場合もコンパイルできますが、c.reset() と呼ぶと E0034 になります。Resettable::reset(&mut c) と書けば直ります。
関連するエラー
Section titled “関連するエラー”- E0034:同じ名前のメソッドが複数:呼び出しのところで後から出るエラーです。2つのトレイトが同じ名前のメソッドを持つときのエラーでもあります。
- E0428:同じ名前の重複定義:モジュールの中のアイテムについての同じ間違いです。
struct Counterが2つ、自由関数fn mainが2つ、などです。 - E0599:そのメソッドが見つからない:逆の問題で、メソッドがそもそも存在しない場合です。
- E0255:名前がインポートですでに使われているとE0252:名前が2回インポートされている:
useが原因の名前の重複です。 - コンパイルエラーの一覧:エラー解説ページの全リストです。
理解度チェック
Section titled “理解度チェック”理解度チェック
1. ある型に impl ブロックが2つあります。どちらにも reset というメソッドを書けますか?
2. area() と area(scale: f64) の両方が欲しいとき、Rust ではどうしますか?
3. impl<T> Wrapper<T> と impl Wrapper<i32> の両方が describe を定義すると、なぜぶつかるのですか?
スコア: 0 / 3
さらに詳しく
Section titled “さらに詳しく”- E0592 — Rust error codes index(英語):このエラーの公式の説明です。
- メソッド記法 — The Rust Programming Language 日本語版:メソッド、関連関数、そして「複数のimplブロック」について。
- ジェネリックなデータ型 — The Rust Programming Language 日本語版:
impl<T> Point<T>とimpl Point<f32>を並べて書く例と、その関係です。 - Implementations — The Rust Reference(英語):固有の
implブロック(inherent impl)と、その関連アイテムの正確なルールです。 - E0034 — Rust error codes index(英語):後から出る「multiple applicable items」エラーの、公式のページです。