ブランケットトレイト実装

トレイトがローカルであれば、好きなだけ多くの型に対して実装できます。これをどこまで進められるでしょうか?

// 著作権 2025 Google LLC
// SPDX-License-Identifier: Apache-2.0

pub trait PrettyPrint {
    fn pretty_print(&self);
}

// ブランケット実装です! 何かが Display を実装していれば、
// PrettyPrint も実装していることになります。
impl<T> PrettyPrint for T
where
    T: std::fmt::Display,
{
    fn pretty_print(&self) {
        println!("{self}")
    }
}
  • トレイトの定義箇所では、トレイト実装の対象は何でもかまわず、境界のない T も含まれます。

    何も分からない T に対しては何もできないため、これは一般的ではありません。

  • 条件付きブランケット実装は、はるかに有用で、見かけたり自分で書いたりする機会も多いでしょう。

    こうした実装では、impl <T: Display> ToString for T {...} のように、トレイトに境界が付きます。

    上の例では、Display を実装するすべての型に対するブランケット実装があります。この実装がトレイト境界から利用できる情報は 1 つで、Display::fmt を実装していることです。

    これは、コンソールに整形表示する実装を書くには十分です。

  • ただし、この種の実装には注意してください。結果として、下流のユーザーがより意味のある実装を行えなくなる可能性があります。

    上の例を Debug 向けに書いていないのは、そうするとほとんどすべての型が PrettyPrint を実装することになってしまうからです。また、Debug は意味的に Display と似ていません。Debug は、より人間が読みやすいものではなく、デバッグ出力を目的としています。

参照:

  • https://doc.rust-lang.org/reference/glossary.html#blanket-implementation