📦
コレクション — ArrayとHashがVecとHashMapになると
型が固定された世界の配列とハッシュマップ
Vec — RubyのArray
# Ruby — どんな型でも混ぜられる
arr = [1, "hello", true, nil]
// Rust — 1つの型のみ
let arr: Vec<i32> = vec![1, 2, 3];
// vec![1, "hello"] // コンパイルエラー
基本操作:
let mut v = vec![1, 2, 3];
v.push(4); // Ruby: arr.push(4) / arr << 4
v.pop(); // Ruby: arr.pop
v.len(); // Ruby: arr.length
v[0]; // Ruby: arr[0]
v.contains(&3); // Ruby: arr.include?(3)
v.is_empty(); // Ruby: arr.empty?
HashMap — RubyのHash
# Ruby
h = { name: "sehwa", age: 30 }
h[:name] # "sehwa"
// Rust
use std::collections::HashMap;
let mut h = HashMap::new();
h.insert("name", "sehwa");
h.insert("age", "30"); // 値も同じ型でなければならない
h.get("name"); // Option<&&str> — なければNone(nilではない!)
Rubyでh[:missing]はnilを返す。Rustでh.get("missing")はNoneを返す。Option処理が必要。
entry API — RubyのHash.new(default)に対応
# Ruby
counts = Hash.new(0)
words.each { |w| counts[w] += 1 }
// Rust
let mut counts = HashMap::new();
for word in &words {
*counts.entry(word).or_insert(0) += 1;
}
entry().or_insert(0)は「キーがなければ0を挿入し、いずれにせよ値の可変参照を返す」。
HashSet — RubyのSet
use std::collections::HashSet;
let s: HashSet<i32> = [1, 2, 3].iter().cloned().collect();
s.contains(&2); // true
ソート
let mut v = vec![3, 1, 2];
v.sort(); // [1, 2, 3] — 元を変更
users.sort_by(|a, b| a.age.cmp(&b.age));
Rustのsort()は元を変更する(mut必要)。元を保持するなら.clone()してからソート。
型が1つという制約
Rubyの[1, "hello", true]は使えない。複数の型を入れたければenumを使う。
enum Value {
Int(i32),
Str(String),
Bool(bool),
}
let mixed: Vec<Value> = vec![
Value::Int(1),
Value::Str("hello".into()),
Value::Bool(true),
];
面倒だが、取り出す時に型チェックが強制されるのでランタイムエラーが減る。
キーポイント
1
Array → Vec<T>、Hash → HashMap<K, V> — 型パラメータ必須
2
Hash.get()はOption返却 — nilの代わりにNone
3
entry().or_insert()でデフォルト値パターンを実装
4
複数の型を入れるにはenumでラップ
メリット
- ✓ 型が固定されているので取り出す時に型変換が不要
- ✓ get()がOption返却なのでインデックスエラーがない
デメリット
- ✗ Rubyの柔軟な配列操作に比べて型制約が不便
- ✗ HashMapの初期化がRubyのHashリテラルより冗長
ユースケース
データグルーピング/カウント — HashMap + entryパターン
APIレスポンスパース — Vec<Item>で型安全に