Rustのトレイト入門:実装方法を基礎から解説
はじめに
Rustを学んでいると trait という言葉が頻繁に出てきます。トレイトはRustの型システムの中心的な概念で、「型がどんな操作をサポートするか」を定義する仕組みです。
この記事ではトレイトの基本的な書き方と実装方法を解説します。
トレイトとは
トレイトは「メソッドのシグネチャの集まり」を定義するものです。他の言語のインターフェースに近い概念です。
trait Animal { fn name(&self) -> &str; fn sound(&self) -> &str; }
このトレイトを実装した型は name() と sound() を持つことが保証されます。
トレイトを実装する
impl トレイト名 for 型名 の形で実装します。
struct Dog; struct Cat; impl Animal for Dog { fn name(&self) -> &str { "犬" } fn sound(&self) -> &str { "ワン" } } impl Animal for Cat { fn name(&self) -> &str { "猫" } fn sound(&self) -> &str { "ニャー" } }
実装した型は同じように使えます。
fn introduce(animal: &impl Animal) { println!("{}は{}と鳴きます", animal.name(), animal.sound()); } introduce(&Dog); // 犬はワンと鳴きます introduce(&Cat); // 猫はニャーと鳴きます
デフォルト実装
トレイト側にデフォルトの実装を持たせることができます。実装する型はオーバーライドしても、そのまま使ってもOKです。
trait Greet { fn name(&self) -> &str; fn hello(&self) { println!("こんにちは、{}です", self.name()); } } struct User { name: String, } impl Greet for User { fn name(&self) -> &str { &self.name } // hello() はデフォルト実装をそのまま使う } let user = User { name: "太郎".to_string() }; user.hello(); // こんにちは、太郎です
トレイト境界:引数の型制約
関数の引数にトレイトを使って「このトレイトを実装している型なら何でも受け取る」という制約を書けます。
impl Trait 構文(シンプル)
fn print_name(animal: &impl Animal) { println!("{}", animal.name()); }
ジェネリクス + トレイト境界
fn print_name<T: Animal>(animal: &T) { println!("{}", animal.name()); }
複数のトレイトを要求する場合は + でつなぎます。
fn show<T: Animal + std::fmt::Debug>(animal: &T) { println!("{:?}", animal); println!("{}", animal.name()); }
where 句(複雑なときに読みやすい)
fn show<T>(animal: &T) where T: Animal + std::fmt::Debug, { println!("{:?}", animal); }
戻り値にトレイトを使う
impl Trait は戻り値の型にも使えます。
fn make_animal() -> impl Animal { Dog }
ただし返す型は1種類に固定されます。条件によって Dog か Cat を返したい場合はトレイトオブジェクト(Box<dyn Animal>)を使います。
fn make_animal(is_dog: bool) -> Box<dyn Animal> { if is_dog { Box::new(Dog) } else { Box::new(Cat) } }
まとめ
| 概念 | 書き方 |
|---|---|
| トレイト定義 | trait Name { fn method(&self); } |
| トレイト実装 | impl Trait for Type { ... } |
| デフォルト実装 | トレイト定義内にメソッド本体を書く |
| 引数の制約 | fn f(x: &impl Trait) / fn f<T: Trait>(x: &T) |
| 動的ディスパッチ | Box<dyn Trait> |
Rustの標準ライブラリには便利なトレイトが多数用意されています。よく使うものは「Rustでよく使う標準トレイト一覧:Display・Debug・Clone・Iteratorなど」で解説しています。
RustのStringと&strの違いは「RustのStringと&strの違い:使い分けと変換方法まとめ」を参照してください。