トレイト境界 structとimpl どちらにつけるべきか
ジェネリクス in Rust
ジェネリック型パラメーターを持つ関数や型をジェネリック(generic)と呼ぶ。
参照: ジェネリクス Generics - あるマのメモ書き
Rustでは、型の宣言側(struct, enum, trait)と実装側(impl)、両方においてジェネリック型パラメーターを宣言することができる。
定義側
struct Either<L, R> (L, R);
実装側
impl<L> Either<L, bool> { fn get_left(&self) -> L { self.0 } }
トレイト境界
ジェネリック型パラメーターに対して、特定のトレイトを実装していることを宣言することが出来る。
トレイト境界 at 定義側
型がそのトレイトに対して完全に依存している場合は、その型に対してトレイト境界を付ける。 そうすることで、impl側でもジェネリクスのときは、トレイト境界を強制する。 また具体型を型引数に指定する場合は、トレイトを実装している必要がある。
use std::fmt::Display; struct Show<T: Display> { x: T, } // impl側にもトレイト境界が必要となる impl<T: Display> Show<T> { fn print(&self) { println!("{:#}", self.x); } } // もしくは、型引数にはDisplayトレイトが実装された型のみ取ることが出来る impl Show<String> { ... }
多重定義となるため避ける
ただstruct側で境界を付けることは、それほど多くならないと思われる。 またHaskellでは、実装側で型クラス制約をつける
トレイト境界 at 実装側
特定のトレイトに対してのみ、追加の振る舞いを提供したい場合などが実現できる。
trait GoldCustomer { fn get_store_points(&self) -> i32; } struct Store<T> { customer: T, } // TがGoldCustomerのときのみのメソッドを追加することができる。 impl <T: GoldCustomer> Store { fn choose_reward(customer: T) { } }
sturct側で境界を課さないことで幅広い実装が可能となる。
struct Pair<T> { a: T, b: T, } impl<T> Pair<T> where T: std::ops::Add<T, Output = T>, { fn sum(self) -> T { self.a + self.b } } impl<T> Pair<T> where T: std::ops::Mul<T, Output = T>, { fn product(self) -> T { self.a * self.b } }
まとめ
トレイト境界
- 定義側で境界する
- 特定のトレイトに対して型を依存させる
- 実装側で境界する
- 特定のトレイトに対してのみ振る舞いを追加できる