← トップページに戻る

Rustの勉強[トレイト その2]

ぎじゅつ
Rust

はじめに

NO IMAGEThe Rust Programming Language 日本語版 - The Rust Programming Language 日本語版
を読んでいる

今年は年始から色々悩んでいる気がするがこの取り組みだけは継続したいのでやるぞーーーーー!

前回の振り返り

今日読む場所

お勉強

メモ

デフォルト実装は、自らのトレイトのデフォルト実装を持たない他のメソッドを呼び出すことができます。 このようにすれば、トレイトは多くの有用な機能を提供しつつ、実装者は僅かな部分しか指定しなくて済むようになります。 例えば、Summaryトレイトを、(実装者が)内容を実装しなければならないsummarize_authorメソッドを持つように定義し、 それからsummarize_authorメソッドを呼び出すデフォルト実装を持つsummarizeメソッドを定義することもできます:

#![allow(unused)]
fn main() {
pub trait Summary {
    fn summarize_author(&self) -> String;

    fn summarize(&self) -> String {
        // "({}さんの文章をもっと読む)"
        format!("(Read more from {}...)", self.summarize_author())
    }
}

pub struct Tweet {
    pub username: String,
    pub content: String,
    pub reply: bool,
    pub retweet: bool,
}

impl Summary for Tweet {
    fn summarize_author(&self) -> String {
        format!("@{}", self.username)
    }
}
}

デフォルト実装を、そのメソッドをオーバーライドしている実装から呼び出すことはできないことに注意してください。

引数としてのトレイト

トレイトを定義し実装する方法はわかったので、トレイトを使っていろんな種類の型を受け付ける関数を定義する方法を学んでいきましょう。

たとえば、Listing 10-13では、NewsArticleとTweet型にSummaryトレイトを実装しました。 ここで、引数のitemのsummarizeメソッドを呼ぶ関数notifyを定義することができます。ただし、引数itemはSummaryトレイトを実装しているような何らかの型であるとします。 このようなことをするためには、impl Trait構文を使うことができます。

pub trait Summary {
   fn summarize(&self) -> String;
}

pub struct NewsArticle {
   pub headline: String,
   pub location: String,
   pub author: String,
   pub content: String,
}

impl Summary for NewsArticle {
   fn summarize(&self) -> String {
       format!("{}, by {} ({})", self.headline, self.author, self.location)
   }
}

pub struct Tweet {
   pub username: String,
   pub content: String,
   pub reply: bool,
   pub retweet: bool,
}

impl Summary for Tweet {
   fn summarize(&self) -> String {
       format!("{}: {}", self.username, self.content)
   }
}

pub fn notify(item: &impl Summary) {
   println!("Breaking news! {}", item.summarize());
}

引数のitemには、具体的な型の代わりに、implキーワードとトレイト名を指定します。 この引数は、指定されたトレイトを実装しているあらゆる型を受け付けます。

impl Trait構文は単純なケースを解決しますが、実はより長いトレイト境界 (trait bound) と呼ばれる姿の糖衣構文 (syntax sugar) なのです。 それは以下のようなものです

gemini回答

Rustの「トレイト境界(Trait Bound)」、最初は少し戸惑いますよね。

簡単に言うと、これは**「この関数に渡すデータは、最低限これだけの『特技(機能)』を持っていてくれなきゃ困るよ!」**という、型に対する「条件(制約)」のことです。

たとえ話とコードの比較で整理してみましょう。

  1. 「何でもいい」は困る
    もし、関数が完全な「何でもいい(ジェネリックな型 T)」を受け取ってしまうと、関数の中で何もできなくなります。
// エラーになる例
fn notify<T>(item: &T) {
    println!("{}", item.summarize()); // エラー!Tがsummarizeできるか分からない
}

「どんな型でもいいよ」と言いつつ、中で summarize という特定のメソッドを使おうとすると、コンピュータは「数値だったらどうするの? summarize なんて持ってないよ!」と怒るわけです。
そこで、「Summary という特技(トレイト)を持っている型 T なら、何でもいいよ」という条件を付けます。これがトレイト境界です。

  1. impl Trait と T: Trait の違い

まとめ