Rust error[E0277]: トレイト境界 `T: SomeTrait` が満たされていない場合の修正方法

intermediate🦀 Rust2026-03-18| Rust 1.60+、任意のOS(Linux/macOS/Windows)、cargo build または rustc

Error Message

error[E0277]: the trait bound is not satisfied
#rust#trait#generic#bound

エラーの発生

遅い時間に、きれいなジェネリック関数を書き、cargoがクレートの半分をコンパイルした後、こんなエラーが表示されます:

error[E0277]: the trait bound `T: std::fmt::Display` is not satisfied
  --> src/main.rs:4:20
   |
4  |     println!("{}", value);
   |                    ^^^^^ `T` cannot be formatted with the default formatter
   |
   = help: the trait `std::fmt::Display` is not implemented for `T`
   = note: required by a bound in `core::fmt::Display`
help: consider restricting type parameter `T`
  |
1 | fn print_value(value: T) {
   |                 ++++++++++++++++++++

コンパイラは珍しく親切で、修正方法まで示してくれています。あとはそれを適用するだけです。

何が起きたのか

T に対してジェネリック関数を書き、フォーマット・クローン・比較など、T が特定のトレイトを実装している場合にのみ存在する機能を使おうとしました。Rust はコンパイル時にそれを検証できないため、拒否します。

よくあるトリガー(遭遇頻度の高い順):

  • println!("{}", value) の使用 — Display が必要
  • println!("{:?}", value) または dbg!() の使用 — Debug が必要
  • .clone() の呼び出し — Clone が必要
  • == または != の使用 — PartialEq が必要
  • <> によるソート — PartialOrd が必要

簡単な修正:トレイト境界を追加する

コンパイラの help: 行にすでに追加すべき内容が示されています。型パラメータに直接トレイト境界を付けましょう。

修正前(エラーあり):

fn print_value<T>(value: T) {
    println!("{}", value); // E0277: T は Display を実装していない
}

修正後(正常):

fn print_value<T: std::fmt::Display>(value: T) {
    println!("{}", value); // コンパイル成功
}

先頭に use インポートを追加すると、シグネチャが読みやすくなります:

use std::fmt::Display;

fn print_value<T: Display>(value: T) {
    println!("{}", value);
}

複数の境界:+ を使う

T が複数のトレイトを満たす必要がある場合は、+ でつなぎます:

use std::fmt::{Debug, Display};

fn log_value<T: Display + Debug + Clone>(value: T) {
    let copy = value.clone();
    println!("display: {}", value);
    println!("debug:   {:?}", copy);
}

where 句:シグネチャが長くなるとき

複数の型パラメータに複数の境界を重ねると、関数シグネチャが長くなりすぎます。そういう場合は where 句に移動しましょう:

// 読みにくい:
fn compare_and_show<T: PartialOrd + Display, U: Debug + Clone>(a: T, b: U) { ... }

// where を使うとすっきり:
fn compare_and_show<T, U>(a: T, b: U)
where
    T: PartialOrd + Display,
    U: Debug + Clone,
{
    if a > b { println!("{}", a); }
    println!("{:?}", b.clone());
}

where 句を使えば、インラインでは書けないもの——関連型への境界や Vec<T>: Display のような構造——も表現できます。

根本的な解決策:よく使うトレイトを derive する

渡す構造体や列挙型を自分で定義している場合、#[derive] が最も簡潔な方法です。手動実装不要で、型が自動的に境界を満たします:

#[derive(Debug, Clone, PartialEq, PartialOrd)]
struct Score {
    player: String,
    value: u32,
}

fn print_score<T: Debug + Clone>(item: T) {
    let copy = item.clone();
    println!("{:?}", copy);
}

fn main() {
    let s = Score { player: "Alice".into(), value: 42 };
    print_score(s); // 動作する — Score は Debug + Clone を derive している
}

自動 derive できるトレイト:

  • Debug{:?} フォーマット
  • CloneCopy — 値の複製
  • PartialEqEq — 等値チェック
  • PartialOrdOrd — 順序付けとソート
  • Hash — HashMap のキーとして使用
  • Default — ゼロ値または空の値

Display は例外で、手動実装が必要です。カスタム出力フォーマットに共通のデフォルトはないためです。

構造体と impl ブロックにも境界が必要

E0277 は独立した関数だけの問題ではありません。impl ブロックを持つジェネリック構造体では、impl 自体に境界を宣言する必要があります:

struct Wrapper<T> {
    inner: T,
}

// 誤り:境界なしで Display を使用している
impl<T> Wrapper<T> {
    fn show(&self) {
        println!("{}", self.inner); // E0277
    }
}

// 正しい:impl ブロックに境界を付ける
impl<T: std::fmt::Display> Wrapper<T> {
    fn show(&self) {
        println!("{}", self.inner);
    }
}

制約を狭く保つために impl ブロックを分割することもできます。境界が必要なメソッドは別の impl<T: Bound> ブロックに入れ、それ以外は通常の impl<T> に残します。制約が明確になり、監査しやすくなります。

境界を追加できないとき:トレイトオブジェクト

型パラメータを制御できない場合——サードパーティの型、混在するコレクション、プラグイン方式の API——では、境界の代わりにトレイトオブジェクト(dyn Trait)を使います:

fn print_any(value: &dyn std::fmt::Display) {
    println!("{}", value);
}

fn main() {
    print_any(&42);
    print_any(&"hello");
    print_any(&3.14);
}

トレイトオブジェクトは動的ディスパッチを使います——モノモーフ化されたジェネリクスのゼロコストと比べて、呼び出しごとにわずかな実行時コストがかかります。またオブジェクト安全でないトレイトには使えません。しかしジェネリクスが使えないときは、これで解決できます。

修正の確認

E0277 が解消されたことを確認します:

cargo build 2>&1 | grep E0277
# 何も表示されなければOK

cargo build
# 出力例: Compiling ... / Finished ...

さらに確実にするために、実際の型でコールサイトの境界が保たれているか簡単なテストを追加しましょう:

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_print_value() {
        print_value(42);       // i32 は Display を実装している
        print_value("hello");  // &str は Display を実装している
    }
}

cargo test
# running 1 test
# test tests::test_print_value ... ok

チートシート

  • インライン境界fn foo<T: Trait>(x: T) — シンプル、1〜2つのトレイトに
  • where 句fn foo<T>(x: T) where T: Trait — 複数の境界や複雑な型に
  • Derive:構造体に #[derive(Debug, Clone)] — 型を自分で定義しているとき
  • トレイトオブジェクト&dyn Trait または Box<dyn Trait> — ジェネリクスが使えないとき

Related Error Notes