コンテンツにスキップ

Rustのエラー E0405: cannot find trait in this scope(トレイトが見つからない)

E0405 は、その場所からは Rust に見えないトレイトの名前を書いた、という意味のエラーです。よくあるのは、構造体を表示できるようにしようと impl Display for Point と書いたのに、Display をインポートしていない場合です。トレイトの名前のタイプミスや、別のモジュールにあるトレイトでも、同じエラーになります。rustc がたいてい必要な use の行をそのまま表示してくれるので、いちばん直しやすいエラーの1つです。Rust 1.93 で E0425 に変わった E0412(「cannot find type」、型が見つからない)と違って、このコードは変わっていません。rustc 1.97 でも、トレイトについては E0405 と表示されます。このシリーズのほかのエラーは、Rustのコンパイルエラー解説を見てください。

error[E0405]: cannot find trait `Display` in this scope
--> src/main.rs:6:6
|
6 | impl Display for Point { // ❌ `Display` was never imported
| ^^^^^^^ not found in this scope
|
help: consider importing this trait
|
1 + use std::fmt::Display;
|

「cannot find trait Display in this scope」は「このスコープにトレイト Display が見つからない」という意味です。

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

struct Point {
x: i32,
y: i32,
}
impl Display for Point { // ❌ `Display` was never imported
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}
fn main() {
let p = Point { x: 3, y: 4 };
println!("{}", p);
}

コメントは「Display を一度もインポートしていない」という意味です(エラーになる例なので、コードはそのままにしています)。

^^^^^^^ は6行目の Display を指していて、「not found in this scope」(このスコープに見つからない)というラベルが付いています。Rust はそこから見えるすべてのトレイトを調べました。このモジュールで定義したもの、use の行で持ち込んだもの、そしてプレリュード(prelude)にある少しのものです。そのどれも Display という名前ではありませんでした。

help: は、それがどこにあるかを教えてくれています。use std::fmt::Display; です。1 + は「新しい1行目としてこれを追加する」、つまりファイルの先頭に書く、という意味です。

println! はいつも Display を使っているのに、なぜ見つからないのでしょうか。プレリュードとは、どの Rust のファイルでも最初から使える名前のリストです。そこには Clone、Iterator、From、Default などのふだん使うトレイトが入っていますが、Display、Error、Hash、Write は入っていません。これらはそれぞれのモジュール(std::fmt、std::error、std::hash、std::io)にあり、実装するときにインポートします。

レシピに「Display の鍋を使う」と書いてあるようなものです。料理する人はコンロの上の鍋なら知っていますが、その鍋は戸棚の中にあって、レシピにはどの戸棚かが書いてありません。

Rust は名前を探すとき、標準ライブラリ全体やプロジェクト全体を探し回ったりはしません。見るのはスコープの中にあるものだけです。つまり、自分のアイテム、use の行、プレリュードです。こうすることで、名前の意味が予測しやすくなります。std の中だけでも Write や Error という名前のトレイトはいくつもありますし、あとで追加したクレートがさらに持ち込むかもしれません。もし Rust が勝手に1つを選んだら、依存クレートを追加しただけで、コードの意味が黙って変わってしまうかもしれません。

だから、どのトレイトのつもりなのかを、ファイルの先頭で1回だけ書きます。探す作業は help の行がやってくれて、見つけたトレイトを提案してくれます。use の仕組みはモジュールを、Display についてはよく使うトレイトを見てください。

直し方1:rustc が提案する use の行を追加する

Section titled “直し方1:rustc が提案する use の行を追加する”

help に consider importing this trait(このトレイトをインポートすることを検討してください)と出ているときは、これを選びます。ほとんどの場合はこれです。その行をファイルの先頭にコピーします。

use std::fmt::Display;
struct Point {
x: i32,
y: i32,
}
impl Display for Point {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}
fn main() {
let p = Point { x: 3, y: 4 };
println!("{}", p);
println!("Ferris の位置は {}", Point { x: -1, y: 7 });
}

実行結果:

(3, 4)
Ferris の位置は (-1, 7)
▶ 実際に試してみよう

rustc がいくつものインポートを提案してきたら、選ぶ前によく読みましょう。std::fmt::Write と std::io::Write のように、たまたま名前が同じなだけの別のトレイトです。

直し方2:モジュールをインポートして fmt::Display と書く

Section titled “直し方2:モジュールをインポートして fmt::Display と書く”

Display を実装するときは、これを選びます。どうせ同じモジュールの Formatter と Result も必要になるからです。std::fmt を1回インポートして、3つすべての前に fmt:: を書きます。実際の Rust のコードで、いちばんよく見るスタイルです。

use std::fmt;
struct Point {
x: i32,
y: i32,
}
impl fmt::Display for Point {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}
fn main() {
let p = Point { x: 3, y: 4 };
let label = p.to_string();
println!("{} は {} 文字です", label, label.len());
}

実行結果:

(3, 4) は 6 文字です
▶ 実際に試してみよう

Display を実装すると、.to_string() もおまけで使えるようになります。use をまったく書かずに impl std::fmt::Display for Point と書くこともできます。1回だけ使うなら、それで十分です。

直し方3:トレイトの名前のつづりを直す

Section titled “直し方3:トレイトの名前のつづりを直す”

rustc が a trait with a similar name exists(似た名前のトレイトがある)と言っているときは、これを選びます。トレイトの名前は大文字と小文字を区別するので、shape、Shap、Shape は3つの別々の名前です。

error[E0405]: cannot find trait `Shap` in this scope
--> src/main.rs:9:6
|
1 | trait Shape {
| ----------- similarly named trait `Shape` defined here
...
9 | impl Shap for Square { // ❌ typo: Shap
| ^^^^
|
help: a trait with a similar name exists
|
9 | impl Shape for Square { // ❌ typo: Shap
| +

「similarly named trait Shape defined here」は「似た名前のトレイト Shape はここで定義されている」という意味です。エラーになるプログラムはこちらです(コメントは「タイプミス:Shap」という意味です)。

trait Shape {
fn area(&self) -> f64;
}
struct Square {
side: f64,
}
impl Shap for Square { // ❌ typo: Shap
fn area(&self) -> f64 {
self.side * self.side
}
}
fn main() {
let s = Square { side: 3.0 };
println!("{}", s.area());
}

9行目の下の + が、足りない文字を示しています。それを足しましょう。

trait Shape {
fn area(&self) -> f64;
}
struct Square {
side: f64,
}
impl Shape for Square {
fn area(&self) -> f64 {
self.side * self.side
}
}
fn main() {
let s = Square { side: 3.0 };
println!("{}", s.area());
}

実行結果:

9
▶ 実際に試してみよう

似た名前もインポートの提案もないなら、そのトレイトはまだ存在していません。先に trait Shape { ... } で定義しましょう。トレイトを定義するを見てください。

直し方4:自分のトレイトをそのモジュールからインポートする

Section titled “直し方4:自分のトレイトをそのモジュールからインポートする”

トレイトは自分のものだけれど、別のモジュールにあるときは、これを選びます。モジュールはそれぞれ自分のスコープを持っているので、mod shapes の中のトレイトは、インポートするまで main.rs のトップレベルからは見えません。

error[E0405]: cannot find trait `Shape` in this scope
--> src/main.rs:11:6
|
11 | impl Shape for Square { // ❌ `Shape` lives in `mod shapes`
| ^^^^^ not found in this scope
|
help: consider importing this trait
|
1 + use crate::shapes::Shape;
|

エラーになるプログラムはこちらです(コメントは「Shape は mod shapes の中にある」という意味です)。

mod shapes {
pub trait Shape {
fn area(&self) -> f64;
}
}
struct Square {
side: f64,
}
impl Shape for Square { // ❌ `Shape` lives in `mod shapes`
fn area(&self) -> f64 {
self.side * self.side
}
}
fn main() {
println!("{}", Square { side: 2.0 }.area());
}

use の行を追加して、トレイトが pub になっているかも確かめましょう。pub がないと、インポートそのものが E0603「trait is private」(トレイトが非公開)で失敗します。

mod shapes {
pub trait Shape {
fn area(&self) -> f64;
}
}
use crate::shapes::Shape;
struct Square {
side: f64,
}
impl Shape for Square {
fn area(&self) -> f64 {
self.side * self.side
}
}
fn main() {
println!("{}", Square { side: 2.0 }.area());
}

実行結果:

4
▶ 実際に試してみよう

クレートのトレイトも同じです。impl Serialize for User と書くには、先に use serde::Serialize; が必要です。何もしなければすべて非公開を見てください。

main やヘルパー関数での Box<dyn Error>。 インポートなしで fn run() -> Result<(), Box<dyn Error>> と書くと、cannot find trait `Error` in this scope(このスコープにトレイト Error が見つからない)になり、help: consider importing this trait と use std::error::Error; が表示されます。直し方:その行を追加するか、Box<dyn std::error::Error> と書きます。独自のエラー型を見てください。

ジェネリクスのトレイト境界。 fn print_all<T: Display>(items: &[T]) でも、境界のところを指して Display について同じ E0405 が出ます。直し方:use std::fmt::Display; です。ジェネリクスを見てください。

Debug では別のコードになる。 インポートなしで impl Debug for Point や T: Debug と書くと、E0405 ではなく E0404 になります。expected trait, found derive macro `Debug` (トレイトを期待したのに、derive マクロ Debug が見つかった)です。プレリュードには #[derive(Debug)] のマクロは入っていますが、Debug トレイトは入っていないのです。直し方:use std::fmt::Debug; と書くか(rustc が提案してくれます)、独自の書式がいらないなら #[derive(Debug)] を付けるだけにします。

  • E0425:このスコープに値、関数、(Rust 1.93 以降は)型が見つからない。
  • E0412:「cannot find type」(型が見つからない)の古いコードで、今は E0425 として表示されます。
  • E0433:use std::fmt; を書かずに fmt::Display と書いたときのように、パスの最初の部分が見つからない。
  • E0599:メソッドが見つからない。そのメソッドを提供するトレイトをインポートしていないことがよくあります。
  • E0277:トレイトは見つかったけれど、型がそれを実装していない。
  • E0404:名前は存在するけれど、トレイトではない(Debug マクロなど)。上で紹介しました。
  • コンパイルエラーの一覧:エラー解説ページの全リストです。

理解度チェック

1. impl Display for Invoice と書いたら E0405 が出ました。何が足りないのでしょうか?

2. 次のトレイトのうち、use の行なしで使えるのはどれですか?

3. トレイト Shape は mod shapes の中で定義されていて、impl Shape for Square は main.rs にあります。何が必要ですか?

スコア: 0 / 3