- コレクションは[[ヒープ領域]]を使う
## ベクタで値のリストを保持する
- [[ベクター]]
- `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攻撃]]への耐性も考慮しているため最速ではない