コンテンツにスキップ

Rustのエラー E0404: expected trait, found derive macro(トレイトでないものを使った)

E0404 は、書いた名前は見つかったけれど、それはトレイトではなく、その場所にはトレイトしか書けないという意味のエラーです。その場所とは、impl ___ for Type、T: ___ のようなトレイト境界、そして dyn ___ や impl ___ です。いちばんよくあるケースは、多くの人を驚かせます。インポートなしで impl Debug for Point と書いた場合です。Debug はスコープにあるのですが、それはトレイトではなく #[derive(Debug)] のマクロなのです。もう1つよくあるのは、構造体をトレイトのように使ったケースで、クラスと継承に慣れている人がよくやります。どちらもすぐに直せます。このシリーズのほかのエラーは、Rustのコンパイルエラー解説を見てください。

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 マクロで、トレイトではない」という意味です(エラーになる例なので、コードはそのままにしています)。

^^^^^ は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 はデータと振る舞いを分けています。構造体や列挙型はデータを表します。値が何であるかです。トレイトは振る舞いを表します。型が何をできるかです。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() で大文字にするので、図形の名前は英語のままにしています(日本語には大文字がありません)。トレイト境界については、ジェネリクスとトレイトで説明しています。

引数の型に 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 にはトレイトの本当の名前を書きます。

理解度チェック

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