Rustのエラー E0404: expected trait, found derive macro(トレイトでないものを使った)
E0404 は、書いた名前は見つかったけれど、それはトレイトではなく、その場所にはトレイトしか書けないという意味のエラーです。その場所とは、impl ___ for Type、T: ___ のようなトレイト境界、そして dyn ___ や impl ___ です。いちばんよくあるケースは、多くの人を驚かせます。インポートなしで impl Debug for Point と書いた場合です。Debug はスコープにあるのですが、それはトレイトではなく #[derive(Debug)] のマクロなのです。もう1つよくあるのは、構造体をトレイトのように使ったケースで、クラスと継承に慣れている人がよくやります。どちらもすぐに直せます。このシリーズのほかのエラーは、Rustのコンパイルエラー解説を見てください。
エラーメッセージ
Section titled “エラーメッセージ”error[E0404]: expected trait, found derive macro `Debug` --> src/main.rs:6:6 |6 | impl Debug for Point { // ❌ `Debug` here is the derive macro, not the trait | ^^^^^ not a trait |help: consider importing this trait instead |1 + use std::fmt::Debug; |「expected trait, found derive macro Debug」は「トレイトが来るはずなのに、derive マクロ Debug が見つかった」という意味です。
このエラーが出るのは、次のプログラムです。
struct Point { x: i32, y: i32,}
impl Debug for Point { // ❌ `Debug` here is the derive macro, not the trait fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "Point at ({}, {})", self.x, self.y) }}
fn main() { let p = Point { x: 3, y: 4 }; println!("{:?}", p);}コメントは「ここでの Debug は derive マクロで、トレイトではない」という意味です(エラーになる例なので、コードはそのままにしています)。
エラーの意味
Section titled “エラーの意味”^^^^^ は6行目の Debug を指していて、「not a trait」(トレイトではない)というラベルが付いています。見出しは、Rust が代わりに何を見つけたかを伝えています。Debug という名前の derive マクロです。
じつは、Debug という名前のものは2つあります。#[derive(Debug)] として使うマクロの Debug はプレリュード(prelude)に入っているので、どのファイルからも見えます。一方、impl Debug for ... に必要なトレイトの std::fmt::Debug は、プレリュードに入っていません。そのため Rust は、見える唯一の Debug を見つけ、それがマクロだと気づいて、そこで止まります。help の行が、トレイトを持ち込むためのインポートを教えてくれています。Hash も同じです。expected trait, found derive macro `Hash` となり、use std::hash::Hash; で直ります。
見出しが found struct `Animal` (構造体 Animal が見つかった)のとき(enum や type alias のこともあります)は、自分で定義した型の名前を、トレイトの場所で使っています。
「運転できる人」を頼んだのに、車を渡されたようなものです。車は本物で、関係も深いのですが、仕事が求めていたのは運転できる人、つまり「できること」の集まりでした。それがトレイトです。
Rust がこうする理由
Section titled “Rust がこうする理由”Rust はデータと振る舞いを分けています。構造体や列挙型はデータを表します。値が何であるかです。トレイトは振る舞いを表します。型が何をできるかです。impl Trait for Type はこの2つを結びつけて、「この型はこれらのことができる」と言います。T: Trait のような境界は、「これらのことができる型なら何でも」という意味です。
構造体はできることのリストではないので、こうした場所には書けません。それに、Rust には継承がありません。Animal が構造体のとき、impl Animal for Dog を「Dog は Animal の一種」という意味にはできないのです。いくつかの型で振る舞いを共有したいなら、その共有する部分がトレイトになります。トレイトとジェネリクスを見てください。
また、Rust はマクロとトレイトを別々の名前空間に置いています。だから Debug は、マクロとトレイトの両方の名前になれるのです。derive マクロはプレリュードにあるので、#[derive(Debug)] はどこでも使えます。トレイトのほうは、自分で実装したり境界に使ったりするときだけ、名前を書く必要があります。
直し方1:トレイトをインポートする(use std::fmt::Debug;)
Section titled “直し方1:トレイトをインポートする(use std::fmt::Debug;)”メッセージが found derive macro で、独自の書式にしたいとき、または T: Debug のような境界でトレイトが必要なときは、これを選びます。help の行を追加します。
use std::fmt::Debug;
struct Point { x: i32, y: i32,}
impl Debug for Point { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "位置 ({}, {})", self.x, self.y) }}
fn print_all<T: Debug>(items: &[T]) { for item in items { println!("{:?}", item); }}
fn main() { let p = Point { x: 3, y: 4 }; println!("{:?}", p); print_all(&[Point { x: 0, y: 0 }, Point { x: -1, y: 7 }]);}実行結果:
位置 (3, 4)位置 (0, 0)位置 (-1, 7)よく使われる別の書き方は、use std::fmt; として、fmt::Formatter と fmt::Result を使って impl fmt::Debug for Point と書く方法です。どちらでも大丈夫です。1つのファイルの中では、どちらかに揃えましょう。
直し方2:impl を書く代わりに #[derive(Debug)] を使う
Section titled “直し方2:impl を書く代わりに #[derive(Debug)] を使う”独自の書式がいらないときは、これを選びます。Debug の場合は、たいていこちらです。derive マクロが impl Debug for Point をまるごと書いてくれるので、インポートも必要ありません。
#[derive(Debug)] // ✅ derive マクロが `impl Debug for Point` を書いてくれるstruct Point { x: i32, y: i32,}
fn main() { let p = Point { x: 3, y: 4 }; println!("{:?}", p); println!("{:#?}", p); println!("(0, 0) からのマス目での距離: {}", p.x.abs() + p.y.abs());}実行結果:
Point { x: 3, y: 4 }Point { x: 3, y: 4,}(0, 0) からのマス目での距離: 7使うのはどちらか1つだけにしましょう。#[derive(Debug)] と自分の impl Debug の両方を書くと、「conflicting implementations」(実装が衝突している)というエラー E0119(英語)になります。{:#?} は、見やすい複数行の形式です。くわしくはよく使うトレイトを見てください。
直し方3:共有する振る舞いのためのトレイトを定義する
Section titled “直し方3:共有する振る舞いのためのトレイトを定義する”impl X for Y の行でメッセージが found struct のときは、これを選びます。おそらく、Dog をサブクラスのように「Animal の一種」にしたかったのでしょう。
error[E0404]: expected trait, found struct `Animal` --> src/main.rs:7:6 |7 | impl Animal for Dog { // ❌ `Animal` is a struct, not a trait | ^^^^^^ not a traitエラーになるプログラムはこちらです(コメントは「Animal は構造体で、トレイトではない」という意味です)。
struct Animal { name: String,}
struct Dog;
impl Animal for Dog { // ❌ `Animal` is a struct, not a trait fn name(&self) -> String { String::from("Rex") }}
fn main() { println!("{}", Dog.name());}Rust では、共有する部分はトレイトです。それぞれの型が用意しなければいけないメソッドのリストで、デフォルトのメソッドを付けることもできます。データはそれぞれの構造体に置きます。
trait Animal { // ✅ トレイト:どの動物にもできること fn name(&self) -> String;
fn greet(&self) -> String { // デフォルトのメソッド format!("こんにちは、{} です!", self.name()) }}
struct Dog;
struct Cat { name: String, // データは構造体に置く}
impl Animal for Dog { fn name(&self) -> String { String::from("Rex") }}
impl Animal for Cat { fn name(&self) -> String { self.name.clone() }}
fn main() { println!("{}", Dog.greet()); let tom = Cat { name: String::from("Tom") }; println!("{}", tom.greet());}実行結果:
こんにちは、Rex です!こんにちは、Tom です!1つの構造体にメソッドを足したいだけなら、トレイトはまったくいりません。for を付けずに、impl Dog { fn name(&self) -> String { ... } } と書きます。トレイトを定義するを見てください。
直し方4:構造体を直接受け取るか、トレイトを境界にする
Section titled “直し方4:構造体を直接受け取るか、トレイトを境界にする”エラーが T: Shape や impl Shape のような境界を指していて、Shape が自分の構造体のときは、これを選びます。
error[E0404]: expected trait, found struct `Shape` --> src/main.rs:5:16 |5 | fn describe<T: Shape>(item: &T) { // ❌ `Shape` is a struct, not a trait | ^^^^^ not a traitエラーになるプログラムはこちらです(コメントは「Shape は構造体で、トレイトではない」という意味です)。
struct Shape { sides: u32,}
fn describe<T: Shape>(item: &T) { // ❌ `Shape` is a struct, not a trait println!("a shape");}
fn main() { describe(&Shape { sides: 3 });}境界は「このトレイトを実装している型なら何でも」という意味です。関数が Shape しか受け取らないなら、ジェネリクスをやめて &Shape を受け取ります。いろいろな型で動かしたいなら、共有する振る舞いをトレイトにして、それを境界にします。
struct Shape { sides: u32,}
fn describe(item: &Shape) { // ✅ 構造体そのものを受け取る。ジェネリクスはいらない println!("{} 辺の図形", item.sides);}
trait Named { // ✅ または「名前を持つ型なら何でも」のためのトレイト fn name(&self) -> String;}
impl Named for Shape { fn name(&self) -> String { match self.sides { 3 => String::from("triangle"), 4 => String::from("square"), n => format!("{}-gon", n), } }}
fn shout<T: Named>(item: &T) { // ✅ トレイト境界にはトレイトが必要 println!("{}!", item.name().to_uppercase());}
fn main() { let t = Shape { sides: 3 }; describe(&t); shout(&t); shout(&Shape { sides: 6 });}実行結果:
3 辺の図形TRIANGLE!6-GON!shout は to_uppercase() で大文字にするので、図形の名前は英語のままにしています(日本語には大文字がありません)。トレイト境界については、ジェネリクスとトレイトで説明しています。
よく出会う場面
Section titled “よく出会う場面”引数の型に impl Vec<i32>。 fn total(x: impl Vec<i32>) は expected trait, found struct `Vec` (トレイトが来るはずなのに、構造体 Vec が見つかった)になります。その場所の impl にはトレイトが必要です。直し方:&[i32] か Vec<i32> を受け取ります。どんなコレクションでも受け取りたいなら impl IntoIterator<Item = i32> です。
Box<dyn String>。 dyn にもトレイトが必要なので、これは expected trait, found struct `String` になり、rustc は ToString を提案します。直し方:「文字列にできるものなら何でも」のつもりなら Box<dyn ToString>、そうでなければただの String です。トレイトオブジェクトを見てください。
トレイトの型エイリアス。 type Printable = dyn std::fmt::Display; と書いたあとに impl Printable for A と書くと、expected trait, found type alias `Printable` (トレイトが来るはずなのに、型エイリアス Printable が見つかった)になり、「type aliases cannot be used as traits」(型エイリアスはトレイトとして使えない)というラベルが付きます。直し方:impl にはトレイトの本当の名前を書きます。
関連するエラー
Section titled “関連するエラー”- E0405: cannot find trait in this scope:インポートしていない
Displayのように、名前がそもそも存在しない。E0404 は、名前は存在するけれど種類が違う、という意味です。 - E0277: the trait bound is not satisfied:トレイトは正しいけれど、型がそれを実装していない。
- E0425: cannot find value in this scope:値、関数、型についての、同じような「見つからない」エラー。
- E0107: wrong number of generic arguments:トレイトや型は正しいけれど、
<...>の引数が多すぎるか少なすぎる。 - E0119: conflicting implementations(英語):同じ型に
#[derive(Debug)]と自分のimpl Debugの両方がある。 - コンパイルエラーの一覧:エラー解説ページの全リストです。
理解度チェック
Section titled “理解度チェック”理解度チェック
1. impl Debug for Point で「expected trait, found derive macro Debug」と言われました。独自の書式にしたいなら、いちばん簡単な直し方はどれですか?
2. struct Animal があり、impl Animal for Dog と書きました。Rust は代わりに何を求めていますか?
3. fn show<T: ___>(x: T) の T: のあとに書けるのはどれですか?
スコア: 0 / 3
さらに詳しく
Section titled “さらに詳しく”- E0404 — Rust error codes index(英語):このエラーの公式の説明です。構造体をトレイトとして使った例が載っています。
- トレイト:共通の振る舞いを定義する — The Rust Programming Language 日本語版:トレイトの定義、実装、境界としての使い方です。
std::fmt::Debug— standard library docs(英語):Debugを derive する方法と、手で実装する方法の違いです。- トレイト — Rust By Example 日本語版:いくつかの型で共有するトレイトと、デフォルトのメソッドです。
std::prelude— standard library docs(英語):どのファイルでも使えるもの。Debugの derive マクロは入っていますが、トレイトは入っていません。