Namaran

Code daily. Without assist.

2026-08-25

formatter_dyn.rs

trait Formatter {
    fn format<T: std::fmt::Display>(&self, value: T) -> String;
}

struct Prefixed {
    prefix: String,
}

impl Formatter for Prefixed {
    fn format<T: std::fmt::Display>(&self, value: T) -> String {
        format!("{}{}", self.prefix, value)
    }
}

fn main() {
    let f: Box<dyn Formatter> = Box::new(Prefixed {
        prefix: "> ".to_string(),
    });
    println!("{}", f.format(42));
}

このコードはコンパイルできません。何が問題で、どう直せばよいでしょうか?

Answer

formatがジェネリック型パラメータTを持つメソッドなので、Formatterはオブジェクト安全(dyn互換)ではありません。ジェネリックメソッドは呼ばれる型ごとに別々のコードを生成するので、実行時にただ1つのエントリで済む仮想関数テーブルには載せられないのです。Box<dyn Formatter>を作ろうとした時点でコンパイルエラーになります。ジェネリック引数の代わりにトレイトオブジェクト&dyn Displayを受け取るようにすれば、format自体が単相な(ジェネリックでない)メソッドになり、トレイトはdyn互換になります:

use std::fmt::Display;

trait Formatter {
    fn format(&self, value: &dyn Display) -> String;
}

impl Formatter for Prefixed {
    fn format(&self, value: &dyn Display) -> String {
        format!("{}{}", self.prefix, value)
    }
}

呼び出し側もf.format(&42)のように参照で渡すよう直します。