0Pricing
Learn Rust Coding · レッスン

トレイト境界

トレイトでジェネリック型を制約します。

「トレイト境界」はCoddyKit上の無料Learn Rust Codingレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはLearn Rust Coding学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Learn Rust Codingコースには全4レッスンが含まれています。

Trait Boundの役割

trait boundは、指定されたtraitを実装する型だけにジェネリック型を制限します。これにより、その型がどのような動作を保証するかをコンパイラーに伝えます。

関数を多くの型で使えるまま、ジェネリックコード内でそのtraitのメソッドを利用できるようになります。

インラインBound構文

最も単純な形式では、型パラメータの直後にboundを記述します。T: Traitの場合、TはDisplayを実装している必要があるため、出力できます。

関数内では、Displayが提供する任意のメソッドを呼び出せるようになります。

use std::fmt::Display;

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

独自のTrait

独自のtraitにもboundを使えます。メソッドを持つtraitを定義し、それを基準にジェネリック関数へboundを付けます。

このSummary traitは、文字列を返すsummarizeメソッドを要求します。

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

実装とBoundの指定

型にtraitを実装すると、bound付き関数はそのtraitを実装する任意の型を受け取れるようになります。関数は具体的な型を知らなくてもtraitメソッドを呼び出せます。

この完全なプログラムは、Articleの概要を出力します。

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

struct Article { title: String }

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

fn notify<T: Summary>(item: &T) {
    println!("{}", item.summarize());
}

fn main() {
    let a = Article { title: String::from("Rust") };
    notify(&a);
}

+によるBoundの組み合わせ

+でtraitをつなぐと、複数のtraitを同時に要求できます。ここではTがDisplayとCloneの両方を実装している必要があります。

これで関数は値を出力すると同時に、値をcloneすることもできます。

use std::fmt::Display;

fn process<T: Display + Clone>(value: T) {
    let copy = value.clone();
    println!("{}", copy);
}

引数でのimpl Trait

引数位置の impl Trait 構文は、単純な境界の省略形です。item: &impl Summary は、<T: Summary> パラメータと同じ意味です。

引数が1つだけの場合は簡潔に書けますが、型を再利用する場合は制御性が低くなります。

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

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

impl Trait の返却

impl Trait を返して、具体的な型を隠しながら、その型がトレイトを実装していることを保証することもできます。これはクロージャやイテレータに便利です。

呼び出し側が知るのは、結果が指定されたトレイトを実装していることだけです。

fn make_adder(n: i32) -> impl Fn(i32) -> i32 {
    move |x| x + n
}

fn main() {
    let add5 = make_adder(5);
    println!("{}", add5(10));
}

境界による演算子の使用

演算子はトレイトに対応しています。+ には Add、== には PartialEq、比較には PartialOrd が必要です。これらで境界を指定すると、ジェネリックコードで演算子を使えるようになります。

ここでは、合計を求めるために T が自身との Add を実装している必要があります。

use std::ops::Add;

fn sum<T: Add<Output = T>>(a: T, b: T) -> T {
    a + b
}

デフォルトのトレイトメソッド

トレイトには、メソッドのデフォルト実装を定義できます。実装側はそれを上書きすることも、デフォルトのまま使うこともできます。境界を持つジェネリックコードでは、その時点で有効な実装が使われます。

この Summary にはデフォルトの summarize があり、型側でそのまま使えます。

trait Summary {
    fn summarize(&self) -> String {
        String::from("(no summary)")
    }
}

struct Note;
impl Summary for Note {}

静的ディスパッチと動的ディスパッチ

トレイト境界では静的ディスパッチが使われます。コンパイラはモノモーフィゼーションによって、コンパイル時に正確なメソッドを選択します。一方、dyn Trait は vtable を通じた動的ディスパッチを使います。

境界を使う方が通常は高速です。dyn は速度と引き換えに、バイナリサイズの縮小と実行時の柔軟性を得ます。

ジェネリック構造体の境界

トレイト境界は関数に限られません。構造体の定義時に境界を要求し、すべてのインスタンスがそのトレイトを満たすようにできます。

ここでは、すべての Sortable<T> で要素を比較できることが保証されます。

struct Sortable<T: PartialOrd> {
    items: Vec<T>,
}

確認問題

トレイト境界についての理解度を確認しましょう。

まとめ

トレイト境界はジェネリック型に制約を加え、コンパイラがトレイトのメソッドや演算子の使用を許可できるようにします。+ でトレイトを組み合わせ、引数や戻り値では impl Trait を省略形として使います。

境界ではオーバーヘッドなしの静的ディスパッチが行われます。これは動的ディスパッチを使う dyn Trait とは異なります。

よくある質問

「トレイト境界」レッスンは無料ですか?

はい。「トレイト境界」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Learn Rust Codingコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Learn Rust Codingコースには全4レッスンが含まれています。

「トレイト境界」で何を学びますか?

トレイトでジェネリック型を制約します。 ブラウザで直接実行するハンズオンコードでLearn Rust Codingを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Learn Rust Codingを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのLearn Rust Codingは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。

「トレイト境界」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このLearn Rust Codingレッスンでコードを書いて実行できますか?

はい。すべてのLearn Rust Codingレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. ジェネリック関数
  2. ジェネリックな構造体とenum
  3. トレイト境界
  4. where句と複数の境界
← Learn Rust Codingに戻る