TL;DR: 10秒で解決
デバッグのためにデータを確認したいだけなら、難しく考える必要はありません。ほとんどのデベロッパーは、次の2つの簡単なステップで解決しています。
- structやenumの直上に
#[derive(Debug)]を追加します。 - print文を更新して、デバッグフォーマッタを使用するようにします:
println!("{:?}", my_var);
エンドユーザー向けにきれいな出力が必要な場合は、代わりに Display トレイトを手動で実装する必要があります。
問題点:なぜRustはコンパイルエラーを出すのか
Rustは明示性を重視します。JavaScriptやPythonのような言語とは異なり、Rustは複雑なデータ構造をどのように文字列に変換すべきかを推測しません。i32 や String のような一般的な型には組み込みのフォーマットがありますが、カスタム型は白紙の状態です。
次のようなコードを実行しようとして、この壁に突き当たったことがあるかもしれません。
struct User {
id: u32,
username: String,
}
fn main() {
let user = User { id: 1, username: String::from("alice") };
// この行が E0277 エラーを引き起こします
println!("{}", user);
}
{} を使用すると、Rustに Display トレイトを使用するよう要求することになります。User がどのように表示されるべきかを定義していないため、コンパイラは次のようなメッセージを表示して即座に停止します。
error[E0277]: `User` doesn't implement `std::fmt::Display`
--> src/main.rs:10:20
|
10 | println!("{}", user);
| ^^^^ `User` cannot be formatted with the default formatter
解決策1:Debugトレイト(開発に最適)
Debug トレイトは、開発中に変数を検査するための標準的な方法です。フォーマットのロジックを一行も書くことなく、構造体名とそのすべての内部フィールドを表示できます。
適用方法:
#[derive(Debug)] // 1. この属性を追加するだけ
struct User {
id: u32,
username: String,
}
fn main() {
let user = User { id: 1, username: String::from("alice") };
// 2. 1行で表示する場合は {:?} を、複数行で「きれいな」表示にする場合は {:#?} を使用します
println!("{:?}", user);
}
結果: User { id: 1, username: "alice" }
20以上のフィールドを持つ大きな構造体の場合は、常に {:#?} を使用してください。インデントと改行が追加され、混雑したターミナルでも出力を非常に確認しやすくなります。
解決策2:Displayの実装(ユーザーに最適)
コマンドラインツールを構築している場合、ユーザーは User { id: 1, username: "alice" } のような出力を見たくありません。彼らはただ情報を分かりやすく見たいだけです。アプリごとにニーズが異なるため、Rustではこのロジックを手動で記述する必要があります。
実装方法:
use std::fmt;
struct User {
id: u32,
username: String,
}
impl fmt::Display for User {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// 出力を思い通りにフォーマットする
write!(f, "User #{} ({})", self.id, self.username)
}
}
fn main() {
let user = User { id: 1, username: String::from("alice") };
// これで単純な {} プレースホルダーが完璧に動作します
println!("{}", user);
}
結果: User #1 (alice)
なぜRustはこれを自動的に行わないのか?
安全性とプライバシーが主な理由です。password_hash や private_key を含む構造体を想像してみてください。Rustが自動的に文字列表現を実装してしまうと、機密データが誤ってログやコンソールに漏洩する可能性があります。Debug と Display のどちらかを選択させることで、Rustはどのデータがプログラム外に出るかを開発者が意図的に制御できるようにしています。
作業の確認
エラーが完全に解消されたことを確認するために、以下の手順に従ってください。
cargo checkを実行します。E0277 エラーが消えていれば、トレイトの要件が満たされています。cargo runを実行して、実際の出力を確認します。Debugを使用した場合は、すべてのフィールドが表示されていることを確認します。Displayを使用した場合は、フォーマットがwrite!マクロのロジックと一致しているか確認します。
ネストされた型に注意
Rustのトレイトは再帰的です。構造体にカスタムのサブ構造体が含まれている場合、そのサブ構造体もトレイトを実装している必要があります。忘れてしまうと、親構造体に derive 属性が付いていても、コンパイラはエラーを出します。
#[derive(Debug)]
struct Metadata {
created_at: u64,
}
#[derive(Debug)] // MetadataもDebugを派生させているため、これは動作します
struct Task {
name: String,
meta: Metadata,
}
役立つリソース
- 公式 std::fmt ドキュメント:フォーマットに関する決定版ガイド。
- Rust by Example:DisplayとDebugの視覚的なガイド。
- derive_more クレート:多数の型に対して手動で Display を実装するのに疲れた場合に便利なツール。

