本文へスキップ
BecomeCoder

Rustコース · 第9章 ジェネリクスとトレイト · レッスン37

トレイト ― 振る舞いを定義する

ブラウザで完結

導入

トレイトは「この型は、こういう振る舞い(メソッド)ができる」という約束を定義するものです。「面積を計算できる」「文字列で表現できる」といった能力を型に持たせ、異なる型を同じインターフェースで扱えるようにします。オブジェクト指向の「インターフェース」に相当します。

説明

// トレイト定義:「要約できる」という振る舞い
trait Summary {
    fn summarize(&self) -> String;
}

struct Article {
    title: String,
    body: String,
}

struct Tweet {
    username: String,
    text: String,
}

// Article に Summary を実装
impl Summary for Article {
    fn summarize(&self) -> String {
        format!("{}(記事)", self.title)
    }
}

// Tweet に Summary を実装
impl Summary for Tweet {
    fn summarize(&self) -> String {
        format!("@{}: {}", self.username, self.text)
    }
}

fn main() {
    let a = Article { title: String::from("Rust入門"), body: String::from("...") };
    let t = Tweet { username: String::from("ferris"), text: String::from("🦀") };

    println!("{}", a.summarize());   // Rust入門(記事)
    println!("{}", t.summarize());   // @ferris: 🦀
}
  • trait Summary { fn summarize(&self) -> String; } … 「summarize というメソッドを持つ」という約束(本体は書かず、シグネチャだけ)。
  • impl Summary for ArticleArticle に対して、その約束を具体的に実装します。Tweet にも別の実装を与えられます。
  • 結果、型は違うのに「summarize() できる」という共通の能力を持ちます。

第7章で使った #[derive(Debug)] も、実は「Debug トレイトを自動で実装してくれ」という指示でした。Clone(第4章)や Copy(第3章)もトレイトです。これまで暗黙に使ってきた機能の多くが、トレイトで表現されていたわけです。

トレイトには既定の実装も書けます。

trait Summary {
    fn summarize(&self) -> String {
        String::from("(要約なし)")   // 実装しなければこれが使われる
    }
}

こうしておくと、impl Summary for X {} と空で書くだけで既定の振る舞いが手に入ります。

試すには

新しい構造体 Book を作り impl Summary for Book を書けば、Booksummarize() できるようになります。トレイトは「型の集合に共通の能力を後付けする」仕組みだと捉えると、次のレッスンの使い道が見えてきます。

実際に動かしてみよう

下のエディタにRustを書いて「実行」を押すと、学習用シミュレータが println! の出力を表示します(本物のrustcではなく、教材の範囲を再現した軽量エンジンです)。struct・enum・match・トレイト・Vec/String/HashMap・Option/Result なども動きます。本文の例を書き換えて動かしてみましょう(所有権・借用チェッカーのコンパイルエラーは再現しないため、動きの確認用として使ってください)。

Rust — ブラウザ内で実行(学習用シミュレータ)

Rustの教材サブセットを動かす学習用シミュレータを読み込みます(本物のrustcではなく、動きを再現した軽量な自作エンジンです)。
スクロールして表示された時点でも自動で読み込まれます。