Rust 基礎からマスターまで — 第5回
ジェネリック、トレート、ライフタイムで再利用可能なAPIを作成する
本ブログ作成文は、講義を目指して製作されました。 また、作成された文章の情報も間違っている可能性がありますので、ご注意ください。
ジェネリックは型だけ異なる同じ規則を再利用し、traitは型が提供しなければならない動作を定義します。 ライフタイム表記は、参照間の関係をコンパイラに説明します。
traitでアクションを定義する
trait Damageable {
fn is_alive(&self) -> bool;
fn take_damage(&mut self, amount: i32);
}
fn attack(target: &mut impl Damageable, damage: i32) {
if target.is_alive() {
target.take_damage(damage);
}
}
attack は具体的なタイプがわからなくても Damageable を実装した値を使うことができる。 traitは継承層よりも必要な動作の契約に近い。
ジェネリックとトレートボーン
fn strongest<T>(values: &[T]) -> Option<&T>
where
T: Ord,
{
values.iter().max()
}
TがOrdを実装するという条件があるので、コンパイラは値を比較できることを知っています。 無効なタイプの組み合わせは実行前に拒否されます。
traitの実装
struct Monster {
health: i32,
}
impl Damageable for Monster {
fn is_alive(&self) -> bool {
self.health > 0
}
fn take_damage(&mut self, amount: i32) {
self.health = (self.health - amount).max(0);
}
}
データと共通契約を分離することで、外部タイプにも許容される範囲でトレートを実装することができる。
寿命関係
fn longer<'a>(a: &'a str, b: &'a str) -> &'a str {
if a.len() >= b.len() { a } else { b }
}
'aは、戻り参照が両方の入力参照より長く生きることができないという関係を表します。 参照の実際の寿命を延ばしたり、ランタイムリソースを作成したりする機能ではありません。
ほとんどの単純な寿命はコンパイラによって推論されます。 エラーが発生したときに表記から増やすよりも、戻り値がどの入力を借りるかをAPIの所有関係を最初に確認する必要があります。