Rustのエラー E0252: the name is defined multiple times(2つのuseで名前が衝突)
E0252 は、2つの use の行が同じ名前を持ち込んでいる、という意味のエラーです。お手本のような例が、use std::fmt::Result; と use std::io::Result; の組み合わせです。違う2つの型が、どちらも Result という名前なのです。ほかにも fmt::Error と io::Error、2つの Write トレイトなどがあります。とてもよく出会うエラーですが、直すのは簡単です。たいていは use の行を1つか2つ変えるだけで済みます。rustc 1.97 でも、このエラーは自分のコード E0252 で表示されます。このシリーズのほかのエラーは、Rustのコンパイルエラー解説を見てください。
エラーメッセージ
Section titled “エラーメッセージ”error[E0252]: the name `Result` is defined multiple times --> src/main.rs:2:5 |1 | use std::fmt::Result; | ---------------- previous import of the type `Result` here2 | use std::io::Result; // ❌ a second `Result` | ^^^^^^^^^^^^^^^ `Result` reimported here | = note: `Result` must be defined only once in the type namespace of this modulehelp: you can use `as` to change the binding name of the import |2 | use std::io::Result as OtherResult; // ❌ a second `Result` | ++++++++++++++「the name Result is defined multiple times」は「Result という名前が複数回定義されている」という意味です。
このエラーが出るのは、次のプログラムです。
use std::fmt::Result;use std::io::Result; // ❌ a second `Result`
struct Temperature { celsius: i32,}
impl std::fmt::Display for Temperature { fn fmt(&self, f: &mut std::fmt::Formatter) -> Result { write!(f, "{} °C", self.celsius) }}
fn parse_temperature(text: &str) -> Result<Temperature> { match text.trim().parse() { Ok(celsius) => Ok(Temperature { celsius }), Err(_) => Err(std::io::Error::other("not a number")), }}
fn main() { match parse_temperature("21") { Ok(t) => println!("{}", t), Err(e) => println!("error: {}", e), }}コメントは「2つ目の Result」という意味です(エラーになる例なので、コードはそのままにしています)。
rustc はこのあとにさらに4つのエラーを表示します。E0107(「type alias takes 0 generic arguments」、型エイリアスはジェネリック引数を0個取る)、2つの E0308(型の不一致)、そして E0277 です。これらを追いかける必要はありません。Rust は1つ目のインポートを残したので、ファイルの中の Result はすべて fmt::Result の意味になり、parse_temperature のつじつまが合わなくなっただけです。E0252 を直せば、全部消えます。
エラーの意味
Section titled “エラーの意味”rustc は2つの use の行を指しています。
---の行(1行目):「previous import of the typeResulthere」(型Resultの前のインポートはここ)。こちらが先に来たので、Rust が残すのはこちらです。^^^の行(2行目):「Resultreimported here」(Resultがここで再びインポートされた)。2つ目のインポートが、同じ名前を使おうとしています。help:は、2つ目のインポートの名前をasで変えることを提案しています。
std::fmt::Result と std::io::Result は、本当に別のものです。fmt::Result はただの Result<(), fmt::Error> で、Display や Debug が使います。io::Result<T> は Result<T, io::Error> で、ファイルやネットワークが使います。名前は同じでも、仕事が違うのです。
スマホの連絡先に、自分の母と、パートナーの母の2人を「お母さん」で登録するようなものです。「お母さん」をタップしたとき、スマホはどちらにかければいいのかを知る必要があります。だから片方に言葉を足すか、「お母さん(アナ)」と「お母さん(リー)」のように登録します。
Rust がこうする理由
Section titled “Rust がこうする理由”1つのモジュールの中では、どの名前もちょうど1つのアイテムを指します。use の行は何かをコピーするわけではなく、別の場所にあるアイテムに短い名前を付け足すだけです。2つの use の行が同じ短い名前を付け足すと、そのあとの Result がすべてあいまいになってしまいます。だから Rust は、使う場所ごとに推測するのではなく、インポートのところで止まるのです。
E0252 は、同じ見出しを持つ3兄弟の1つです。E0255 は use の行と自分のアイテムの衝突、E0428 は自分のアイテム2つの衝突です。3つとも、根っこにあるルールは「1つのモジュールでは、1つの名前に1つのもの」です。モジュールも見てください。
直し方1:モジュールをインポートして、fmt:: と io:: を書く
Section titled “直し方1:モジュールをインポートして、fmt:: と io:: を書く”たいていはこれを選びます。Result と Error なら、いつでもこれです。親のモジュールをインポートして、前にモジュール名を書きます。これで、どの Result なのかが、使うたびにわかります。Rust の本(The Rust Book)も、まさにこの場合にこのスタイルを勧めています。
use std::fmt;use std::io;
struct Temperature { celsius: i32,}
impl fmt::Display for Temperature { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{} °C", self.celsius) }}
fn parse_temperature(text: &str) -> io::Result<Temperature> { match text.trim().parse() { Ok(celsius) => Ok(Temperature { celsius }), Err(_) => Err(io::Error::other("数値ではありません")), }}
fn main() { for input in ["21", " -4 ", "warm"] { match parse_temperature(input) { Ok(t) => println!("{}", t), Err(e) => println!("エラー: {}", e), } }}実行結果:
21 °C-4 °Cエラー: 数値ではありませんおまけ:プレリュード(prelude)にあるふつうの Result<T, E> は、インポートしなくても、これまでどおり自由に使えます。
直し方2:片方のインポートの名前を as で変える
Section titled “直し方2:片方のインポートの名前を as で変える”2つの型のうち片方をたくさん使っていて、短い名前を付けたいときは、これを選びます。rustc が提案しているのもこの方法です。OtherResult ではなく、IoResult のように、どこから来たのかがわかる名前を選びましょう。
use std::fmt::Result;use std::io::Result as IoResult;
struct Temperature { celsius: i32,}
impl std::fmt::Display for Temperature { fn fmt(&self, f: &mut std::fmt::Formatter) -> Result { write!(f, "{} °C", self.celsius) }}
fn parse_temperature(text: &str) -> IoResult<Temperature> { match text.trim().parse() { Ok(celsius) => Ok(Temperature { celsius }), Err(_) => Err(std::io::Error::other("数値ではありません")), }}
fn main() { match parse_temperature("21") { Ok(t) => println!("{}", t), Err(e) => println!("エラー: {}", e), }}実行結果:
21 °Cただの Result がこっそり fmt::Result を意味していると、次にコードを読む人が驚くかもしれません。なので、たいていは直し方1のほうがわかりやすくなります。as が活躍するのは、use std::collections::BTreeMap as Map; のような長い名前のときです。
直し方3:トレイトを as _ でインポートする
Section titled “直し方3:トレイトを as _ でインポートする”メソッドを呼ぶためだけにインポートするトレイトなら、これを選びます。お決まりの例が2つの Write トレイトです。fmt::Write を使うと String に write! でき、io::Write を使うと標準出力やファイルに write! できます。両方を名前付きでインポートすると、ぶつかります。
error[E0252]: the name `Write` is defined multiple times --> src/main.rs:2:5 |1 | use std::fmt::Write; | --------------- previous import of the trait `Write` here2 | use std::io::Write; // ❌ a second `Write` | ^^^^^^^^^^^^^^ `Write` reimported here | = note: `Write` must be defined only once in the type namespace of this modulehelp: you can use `as` to change the binding name of the import |2 | use std::io::Write as OtherWrite; // ❌ a second `Write` | +++++++++++++今度は「previous import of the trait Write here」(トレイト Write の前のインポートはここ)と表示されています。エラーになるプログラムはこちらです。
use std::fmt::Write;use std::io::Write; // ❌ a second `Write`
fn main() { let mut report = String::new(); write!(report, "{} tests passed", 12).unwrap();
let mut out = std::io::stdout(); writeln!(out, "{}", report).unwrap();}ここでは、自分で Write と書く場所はありません。必要なのはメソッドだけです。as _ を使うと、トレイトを名前を付けずにインポートできます。だから何もぶつからず、両方のメソッドがちゃんと使えます。
use std::fmt::Write as _;use std::io::Write as _;
fn main() { let mut report = String::new(); write!(report, "{} 件のテストに合格しました", 12).unwrap();
let mut out = std::io::stdout(); writeln!(out, "{}", report).unwrap();}実行結果:
12 件のテストに合格しましたたとえば fn log(w: &mut impl io::Write) のように、どちらかに名前が必要なら、直し方1と組み合わせます。use std::io; と書いて、io::Write を使いましょう。トレイトも見てください。
直し方4:同じアイテムの重複したインポートを消す
Section titled “直し方4:同じアイテムの重複したインポートを消す”2つの行がまったく同じものをインポートしているときは、これを選びます。コードをマージしたあとや、すでにあるインポートをエディタが追加したときによく起こります。
error[E0252]: the name `HashMap` is defined multiple times --> src/main.rs:2:24 |1 | use std::collections::HashMap; | ------------------------- previous import of the type `HashMap` here2 | use std::collections::{HashMap, HashSet}; // ❌ `HashMap` again | ^^^^^^^-- | | | `HashMap` reimported here | help: remove unnecessary importエラーになるプログラムはこちらです(コメントは「また HashMap」という意味です)。
use std::collections::HashMap;use std::collections::{HashMap, HashSet}; // ❌ `HashMap` again
fn main() { let mut stock: HashMap<&str, u32> = HashMap::new(); stock.insert("apples", 3);
let mut sold_out: HashSet<&str> = HashSet::new(); sold_out.insert("pears");
println!("apples: {}, sold out: {}", stock["apples"], sold_out.len());}help が違うことに気づきましたか? as はなく、「remove unnecessary import」(不要なインポートを消す)とだけ書かれています。1行だけ残しましょう。
use std::collections::{HashMap, HashSet};
fn main() { let mut stock: HashMap<&str, u32> = HashMap::new(); stock.insert("apples", 3);
let mut sold_out: HashSet<&str> = HashSet::new(); sold_out.insert("pears");
println!("りんご: {}、売り切れ: {}", stock["apples"], sold_out.len());}実行結果:
りんご: 3、売り切れ: 1RustのHashMapとHashSetとBTreeMapも見てください。
よく出会う場面
Section titled “よく出会う場面”2か所から来る Error。 use std::fmt::Error; と use std::io::Error;、あるいは use std::error::Error; と use anyhow::Error; の組み合わせです。直し方:use std::io; と書いて io::Error を使うか、use anyhow::Error as AnyError; とします。anyhowとthiserrorも見てください。
同じ型の名前を持つ2つのクレート。 use chrono::Duration; と use std::time::Duration; は、日付を扱うプロジェクトでよく見る組み合わせです。直し方:片方は短い名前のまま使い、もう片方は chrono::Duration のようにモジュール名を付けて書きます。日付と時刻も見てください。
グロブインポート。 use std::fmt::*; と use std::io::*; を並べても、use の行では E0252 になりません。問題が起こるのはあとで、最初の Result のところで、コードなしのエラー error: `Result` is ambiguous(Result があいまい)が出ます。直し方:直し方1のように、必要な名前を明示的にインポートします。
関連するエラー
Section titled “関連するエラー”- E0255:
useの行が、同じ名前の自分の構造体、列挙型、関数とぶつかる。 - E0428:自分のアイテム2つが同じ名前を使っている。関数をオーバーロードしようとしたときによく起こります。
- E0432:解決できないインポート。
useの行が、存在しないものを指している。 - E0034:スコープにある2つのトレイトが、型に同じ名前のメソッドを与えている。
- E0308:型の不一致。
Resultがぶつかったあとに続けて出るエラーの1つです。 - コンパイルエラーの一覧:エラー解説ページの全リストです。
理解度チェック
Section titled “理解度チェック”理解度チェック
1. 同じファイルで fmt::Result と io::Result が必要です。ふつう選ぶ、すっきりしたインポートはどれですか?
2. std::fmt::Write と std::io::Write の両方のメソッドが必要ですが、トレイトの名前を自分で書くことはありません。どうすればいいですか?
3. rustc が HashMap is reimported here と表示し、remove unnecessary import を提案しました。何が起きたのでしょうか?
スコア: 0 / 3
さらに詳しく
Section titled “さらに詳しく”- E0252 — Rust error codes index(英語):このエラーの公式の説明です。
asでの直し方と、モジュールのパスでの直し方が載っています。 - 新しい名前をasキーワードで与える — The Rust Programming Language 日本語版:
fmt::Resultとio::Resultの例を、2通りの方法で解決しています。 - Underscore imports — The Rust Reference(英語):
use Trait as _;で、名前を付けずにメソッドだけを持ち込む仕組みです。 - use宣言 — Rust By Example 日本語版:インポートと名前の変更の、短くて実行できる例です。
- Namespaces — The Rust Reference(英語):エラーの注記に出てくる型の名前空間を、トレイトと型が共有している理由です。