実際のところ
structとtrait
乱暴にまとめると、structがデータ構造を定義し、traitが振る舞いを定義するときに使うもの
// データ構造を表現するstruct // 構造はprivateなもので、ユーザーは意識しないで済む struct User { name: String, email: String, age: u8, } // 振る舞いを定義するtrait // インターフェイスをpublicに提供 // 可変サイズとして扱われるのでヒープ領域にて定義(Box) trait UserBehavior { fn login(&self) -> bool; fn update_profile(&mut self) -> Result<(), Box<dyn Error>>; }
で、implキーワードでstructとtraitを紐付け
// とりあえず振る舞いの実験なので常に正常系で…… use std::error::Error; impl UserBehavior for User { fn login(&self) -> bool { true // 常にログイン成功 } // Errorはヒープ領域へのスマートポインタであるBoxを介して活用 // Boxの働きによって、メモリの確保と開放は自動的に管理される fn update_profile(&mut self) -> Result<(), Box<dyn Error>> { Ok(()) // 常に成功 } }
derive属性
structやenumに追加の振る舞いを教え込むマクロを実行してくれるやつ……
自動でtraitの振る舞いをぶっ込んでくれる機能という事
#[derive(Debug)] struct User { name: String, email: String, age: u8, }
とやると、デバッグ用機能を勝手に読み出してくれるようになります。
具体的には、
fn main() { // ユーザーの定義。 // "mut"で変更可能にしとかないとTraitの挙動が意味を為さない let mut user = User { name: String::from("Alice"), email: String::from("alice@example.com"), age: 30, }; // #[derive(Debug)]の霊験で":?"キーワードでデバッグ情報がでてくるようになる println!("User: {:?}", user); }
Builderパターン
少し古い例だと、Builderパターンという手法もあった様子。
ただ、可読性やエラー処理の一貫性から今はderive属性が好まれているとのこと。