- コレクションは[[ヒープ領域]]を使う ## ベクタで値のリストを保持する - [[ベクター]] - `Vec::new()`で生成 - [[vec!]]を使った方が便利 - 要素の追加は`.push` - スコープを抜けると要素もドロップする - [[可変参照]]と[[不変参照]]の併用、基本NGだけどプログラムに実在がない場合はエラーにならないの凄いな。。。 - [[スライス (Rust)|スライス]]や添え字の[[不変参照]]は、参照元の値が変わらない場合のみ成立する - `push`は末尾への追加なので`&v[0]`に影響はなさそう([[ポインタ]]は変わらなそう)だが、`push`のときにメモリの隣にスペースがないなら、別の領域に割り当てられてしまうため`&v[0]`の要素が変わるリスクがあるのでNG - 添え字アクセスではなく `for i in &v`で走査できる ## 文字列でUTF-8でエンコードされたテキストを保持する - 言語の核としての文字列型は `&str` だけ (文字列[[スライス (Rust)|スライス]]) - [[UTF-8]]エンコードされた文字列データへの[[参照]] - [[String]]型は標準ライブラリ - 伸縮可能、可変、[[所有権]]のある[[UTF-8]]エンコードされた文字列型 - [[Rustacean]]が文字列と言ったら、`String`と`&str`のどちらも意味することが多い - 他の文字列型 - [[OsString]] - [[OsStr]] - [[CString]] - [[CStr]] - `to_string`は[[Displayトレイト]]を実装している型なら使用できる - [[Displayトレイト]]には`to_string`実装なさそうだけど...? - `String::from`と`to_string`のどちらを使うかはスタイル次第 (どっちでもいい) - `push_str`で文字列結合 文字列を`+`で連結すると[[所有権]]を失う。つまり ```rust let s3 = s1 + &s2; ``` の実行後に`s1`は[[ムーブ]]されている。これは`+`演算子のシグニチャが以下のようになっていることから。 ```rust fn add(self, s: &str) -> String { ``` と[[📚The Rust Programming Language]]には書いてあるけど、コードを追うと違うんだよな。 ```rust #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] impl Add<&str> for String { type Output = String; #[inline] fn add(mut self, other: &str) -> String { self.push_str(other); self } } ``` ちなみに[[Addトレイト]]は... ```rust #[doc(alias = "+")] pub trait Add<Rhs = Self> { /// The resulting type after applying the `+` operator. #[stable(feature = "rust1", since = "1.0.0")] type Output; /// Performs the `+` operation. /// /// # Example /// /// ``` /// assert_eq!(12 + 1, 13); /// ``` #[must_use] #[stable(feature = "rust1", since = "1.0.0")] fn add(self, rhs: Rhs) -> Self::Output; } ``` いかにも `+` っぽい。`doc(alias = "+")` の力? ```rust use std::ops::Add; #[derive(Debug)] struct User { id: i32, name: String, } impl Add<&User> for User { type Output = User; fn add(self, rhs: &User) -> Self::Output { User{ id: self.id + rhs.id, name: self.name + &rhs.name } } } fn main() { let mimizou = User{ id: 1, name: "みみぞう".to_string() }; let mimico = User{ id: 2, name: "みみこ".to_string() }; let pair = mimizou + &mimico; eprintln!("pair = {:?}", pair); } ``` ```console pair = User { id: 3, name: "みみぞうみみこ" } ``` - [[参照外し型強制]] - 複雑なものは[[formatマクロ]]を使うとよい [[Rust]]で添え字アクセスはできない。下記のコードはエラーになる。 ```rust let s1 = String::from("hello"); let h = s1[0]; ``` - [[String]]は`Vec<u8>`のラッパー - `String::from("Hola").len()` は 4 - [[UTF-8]]でエンコードすると4バイトだから - `String::from("ほーら").len()` は 9 - [[UTF-8]]でエンコードすると9バイトだから - [[String]]の添え字アクセスはバイト単位でしかない - `&hello[0]`は`&hello`という文字列の1バイト目 - ただ[[UTF-8]]の文字列において、それはユーザーの期待通りにはならないことが多い - だからエラーを返してくれる - 3つの観点 - バイト - スカラー値 - [[書記素クラスタ]] - [[Rust]]が添え字アクセスを許さない理由 - 添え字アクセスは`O(1)`のパフォーマンスを期待される - しかし、それが無理 - 実際には中身を走査しないと、スカラー値や[[書記素クラスタ]]としてのN番目の文字は特定できない - だから認めない 誤解を防ぐためにも - `bytes`でバイト列を返す - `chars`でスカラー値を返す ```rust fn main() { let hello = "はろー"; let iter = hello.chars(); for i in iter { // i = 'は' // i = 'ろ' // i = 'ー' eprintln!("i = {:?}", i); } } ``` ## キーとそれに紐づいた値をハッシュマップに格納する - [[HashMap]] - `HashMap::new()`による生成が推奨 - 生成の組み込み[[マクロ]]はない - `zip`を使って生成するわざもある - `insert`で要素を追加 - キーと値はそれぞれがすべて同じ型 - [[HashMap]] (キー&値) の[[所有権]]について - [[Copyトレイト]]を実装している型が値ならコピーされる - [[String]]のように[[所有権]]のある値なら[[HashMap]]に[[ムーブ]]される - [[ライフタイム]]が許すなら、[[参照]]を渡してあげればいいが。。。 - `get`で要素取得 - `for (key, value) in &scores {` でキーと値をまとめて走査も可能 - `insert`を同じキーに対して複数回すると最後の値が上書きの結果残る - [[RustのHashMapでキーが存在しない場合のみ値を挿入]]する方法もある - [[split_whitespace]] - [[HashMap]]は[[DoS攻撃]]への耐性も考慮しているため最速ではない