###### tags: `Rust` `impl` `Generic` `Some(T)` `Reslt<T, E>` `Option<T>` # Rust テキスト学習 4 [TOC] structに対し、関数を呼びたいとき、 baz(bar(foo)) だが、実際呼ばれる順序は foo > bar > baz メソッド構文を使えば、foo.bar().baz(); # impl implブロック : 定義したstructにメソッドを定義するブロック ```rust= struct Circle { x: f64, y: f64, radius: f64, } impl Circle { // メソッドの第一引数は特殊、self, &self, &mut self の3種類がある。 fn area(&self) -> f64 { std::f64::consts::PI * (self.radius * self.radius) } } ``` Circle構造体にarea() 面積を返すメソッドを追加 ## impl メソッドの第一引数 self : 所有権の移動 &self : imutableな参照の借用(一番オススメ) &mut self : mutableな参照の借用 呼び出す側の引数では、 foo.bar() のfooに該当する。 ()内にオブジェクトを書く必要はない。 (C などのオブジェクト指向が言語レベルでサポートされない場合はそういう書き方をする) ## メソッドチェーン foo.bar().baz() のように、メソッドを連続で実行したい。 これは、bar()で .baz()メソッドを持つオブジェクトを作成して返すことで実現できる。 ```rust= impl Circle { fn area(&self) -> f64 { std::f64::consts::PI * (self.radius * self.radius) } fn grow(&self, increment: f64) -> Circle { Circle { x: self.x, y: self.y, radius: self.radius + increment } } } let c = Circle{1.0, 2.0, 3.0}; let grow_c = c.grow(1.0).grow(2.0).grow(3.0); ``` circle.grow() メソッドは、 **半径を引数分だけ増加したCircle** を生成して返すメソッド 単に、Circle structを戻り値とするだけ ## 関連引数(Class method) selfを引数に取らない関数。 呼び出すときは Struct::method という形になる。 Rustではよくあるパターンで、 new() などは関連メソッドであることが多い 構造体ごとにネームスペースを区切れるので同じnewというメソッド名が使える ## Builderパターン Rustメソッドには - methodのオーバーロード(多重定義) - 名前付き引数 - 可変個引数 がない 構造体を作成するとき、特定の値だけ設定して残りはデフォルトの値にしたい。 こういった要望を実現するのに、Builderパターンを使う - StructBuilder構造体(Structと同じデータ構造) - .new() : デフォルト値で作成する - .property(&mut self, arg)->StructBuilder : プロパティ名のメソッド。&mut selfを渡してselfを変更する 変更したい初期propertyの数だけ用意する StructBuilderを返し、メソッドチェーンでさらなる変更 or finalizeができる - .finalize(&self)->Struct : 設定した初期化内容を元に、structを作成して返す。 例. ```rust= impl CircleBuilder { fn new() -> CircleBuilder { CircleBuilder { x: 0.0, y: 0.0, radius: 1.0, } } fn x(&mut self, coordinate: f64) -> &mut CircleBuilder { self.x = coordinate; self } fn y(&mut self, coordinate: f64) -> &mut CircleBuilder { self.y = coordinate; self } fn radius(&mut self, radius: f64) -> &mut CircleBuilder { self.radius = radius; self } fn finalize(&self) -> Circle { Circle { x: self.x, y: self.y, radius: self.radius } } } let c = CircleBuilder::new() .x(1.0) .y(2.0) .radius(2.0) .finalize(); ``` # Vector 動的な配列 Vec\<T>として提供される。 Tはどんな型でも使える vec! マクロで作成できる。 ()でも[]でも使える。(println!マクロでも同様) []で配列の要素にアクセスできるが、指定するインデックスはusize型でないとエラーになる・ ```rust= let v = vec![1,2,3,4,5]; v[1]; let i: usize = 0; let j: i32 = 0; v[i]; v[j]; //error ``` ## イテレーティング v, &v, &mut v の形でfor in 文でイテレーションできる その他多くの便利メソッドはAPIドキュメントから # 文字列 2種類 - &str : 文字列スライス - String : ヒープアロケートの動的配列。 文字列リテラルは &'static str 型 ## 相互変換 &str > String &str.to_string() = &str を String に変換 String::from(&str) = &strを元にStringを作成 String > &str &*string ## 運用面 Stringを使うと、メモリアロケーションが発生するので 必要がないならやらないほうがいい。 ## インデクシング 文字列はUTF-8でエンコードされており、複数バイト対応があるため n番目の文字を探すのは高コストな演算 よって[n]を使ったインデクシングをサポートしていない。 文字列のn番目を見るには、まず - バイト配列、char配列のシーケンスに変換(.as_bytes() or .chars()) - .nth(n) メソッドで取得。 ```rust= let dog = hachiko.chars().nth(1); ``` charsリストの上から走査 ## 連結 String + &str : 可能 String + String : 片方に & をつけて &strに型強制する。 # ジェネリクス 複数の型に対応した関数やデータ型を作成するもの parametric polymorphism とも呼ばれる (与えたパラメータによって、異なる振る舞いを持つこと) ## 例 Option\<T>, Result\<T, E> Rustがstdで提供しているenum型 Option 型 = 取得できないかもしれない値の変数型。 - Some(T) : 取得できた場合の値(要素1タプル構造体)。取得するにはパターンマッチでlet Some(変数)に入れる - None : 取得できないとき ```rust= enum Option<T> { Some(T), None, } ``` Result 型 = 処理が成功したか失敗したかという結果に使える型 - Ok(T) : 処理が成功したときの値 - Err(E) : Errorのときの値(エラーメッセージ(str) とか エラーコード(i32)とか) ```rust= enum Result<T, E> { Ok(T), Err(E), } ``` ジェネリックの第一ジェネリックパラメータは慣例的にTである 二個目以降は気にしない(Resultの場合はE) これらのenum型のバリアントは、単なる1要素タプル構造体であり、 特別な実装などはない(と思われる) ## ジェネリック関数 ```rust= fn takes_anything<T>(x: T){ ... } ``` Tに対してジェネリック 引数にどのような方も取ることができる。 ## ジェネリックStruct ```rust= struct Point<T> { x: T, y: T, } ``` Tに対してジェネリック。要するに、関数名orStruct名の横に \<>で囲ってジェネリックパラメータを指定すると ブロック内で型をジェネリックパラメータで置き換えることができるということである。 implブロックでも、同様に型引数を宣言する。 ```rust= impl<T> Point<T> { fn swap(&mut self) { std::mem::swap(&mut self.x, &mut self.y); } } ``` ## ジェネリックの用途 幅広い Option\<T>, Result\<T>, Vec\<T>など 汎用的にどの方でも使える方を作れる