実践Rust入門 11日目

8章6〜8節まで。

トレイトの利用例。
標準ライブラリのから、ちょっとしたテクニック紹介など。
読むだけスタイルで進めた。

全体的に、こんなことできるんだなぁというのを知れたのは良かった。

しかし、ようやく基礎編が終わったなー。
長かった。

おぼえがき
標準ライブラリのトレイト
  • std::ops::Eqは、std::opts::PartialEqを継承しているだけっぽい
    • std::opts::Eqは、a==b,b==c,a==c,a==aを保証する
    • std::opts::PartialEqは、a==aは保証しない(NaN==Nanなどはfalseになる)
    • ハッシュマップのキーのように、a==aが成り立たないとこまる場面ではEqが要求される
      • この違いはコンパイラじゃ検出できない気がする。実装側がこのルールに従わないと世界が崩壊するのかな − std::marker::Sizedという特殊なトレイトがある
    • コンパイル時に型のサイズが決定できることを表す
    • 定義は空っぽい pub trait Sized { }
      • ユーザは実装することができない
      • コンパイラが自動で付与する
    • Sizedを実装していないと、値の受け渡しができない
    • Sizedを実装していない型は、str[T]など
    • Sizedを実装していない型の参照も特別で、サイズ情報が付与されるため、データサイズが大きくなる
  • Sizedのようなトレイトはopt-in build-in traits(OIBITs)と呼ばれるとのこと
    • Syncも仲間とのこと
演算子オーバーロード

− Rustは既存の演算子(+や-など)に対してならオーバーロードできる
- あくまで既存の演算子だけ。自分で新たに作成することはできない - 各演算子のトレイトを実装することで、オーバーロードする。以下、+のトレイトの例

pub trait Add<RHS = Self> {  
    type Output;  
    fn add(self, rhs: RHS) -> Self::Output  
}  
  • 他にもstd::optsに色々あるっぽい
    • ! : Not 否定
    • * : Deref 参照外し
    • []: Index 配列やハッシュマップのインデクシング
      • Vecのインデクシングは下記ジェネリクスを実装している
        • Index<usize> ... vec[0]
        • Index<RangeFrom<usize>> ... vec[1..]
        • Index<RangeFull> ... vec[..]
トレイトのテクニック
  • StringとInto
    • String型の引数に文字列リテラルを直接渡せない
      • "moji".to_string()とする必要があり面倒。
    • かといって、引数を受け取った側でto_string()を呼ぶのは非効率
    • そんなときは、引数の型をs: impl Into<String>にすると、Stringも文字列リテラルも渡せる
      • 関数内で、let _s = s.into();として展開
  • Intoを使ったオプショナル引数
    • 引数の型をarg1: tmpl Into<Option<usize>>, arg2: Into<Option<usize>>としたとする
    • 呼び出し側は、func(1, None);のように呼び出せる(Some(None))も要らない
  • 以下などはパスネームとして振る舞う(AsRefを実装)
    • Path, PathBuf, &str, String, OsStr, OsString
    • 引数の型を path: impleAsRef<Path>として定義すると良い
      • path.as_ref()などのメソッドが使える
        • File::new(path.as_ref())?;
  • &strとstr
    • トレイトのメソッドの多くは、&self&mut selfを取るらしい
    • そこで、impl SomeTrait for str { fn func(&self); }と実装する
    • そうすれば、let s="str"; s.func();と、参照外しも要らずに自然に呼び出せる
    • という話。
    • Boxなどでも使える
      • let bs = Box::new(*s); bs.func();
  • 外部クレートの型に、外部クレートのトレイトは実装できない
    • しかし、タプル構造体で囲んだ型を定義すればできる
    • struct ExlibStructWrapper(ExLibStruct);みたいな感じ
    • impl ExLibTrait for ExLibStructWrappter{ }で実装
    • let e = ExLibStructWrappter(ExLibStruct::new());として使う − e.method();という感じで、ExLibTraitのメソッドを使えるようになる
  • 列挙型を使った型の混合。便利かも
    • 以下書籍のサンプルコード抜粋(コメントは省略)
#[derive(Debug)]
enum Either<A, B> {  
    A(A),  
    B(B),  
}  

impl<A, B> fmt::Display for Either<A, B>  
where  
    A: fmt::Display,  
    B: fmt::Display,  
{  
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {  
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {  
        match self {  
            Either::A(a) => a.fmt(f),  
            Either::B(b) => b.fmt(f),  
        }  
    }  
}  

let mut v: Vec<Either<bool, i32>> = vec![];  
v.push(Either::A(true));  
v.push(Either::B(1i32));  

for e in v {  
    println!("{}", e);
}  
課題
  • 実践で使えるにはまだまだ先 − 構文に慣れたら一度見直しましょー

実践Rust入門[言語仕様から開発手法まで]

実践Rust入門[言語仕様から開発手法まで]