trifle

技術メモ

Rustの配列に関する雑多なメモ

多重配列の初期化

固定長のarrayの場合は[T; N]という形式なので, 例えば2次元配列の場合は,

let a: [[i32; 3]; 2] = [[0; 3]; 2];
println!("{:?}", a); // [[0, 0, 0], [0, 0, 0]]

let mut b = [[39; 2]; 3]; // 型を省略すると推論される
b[1][1] = 8;
println!("{:?}", b); // [[39, 39], [39, 8], [39, 39]]

で良いと思われる. 初期化せずに値を入れようとするとコンパイルエラーになる.

では, 可変長のvecを二重に包む場合は?と考えてみる. 一つはvec!マクロを使う方法が考えられて,

let mut v1: Vec<Vec<i32>> = vec![vec![99; 3]; 2];
v1[1][1] = 8;
println!("{:?}", v1); // [[99, 99, 99], [99, 8, 99]]

とarrayとほぼ同様の操作ができる.
また, resizeメソッドという, vecを指定の長さに変更する(長さが増える場合は指定された値で足りない要素を埋める)メソッドを2回使えば,

let mut v2: Vec<Vec<i32>> = Vec::new();
v2.resize(4, Vec::new());
for i in 0..4 { v2[i].resize(2, i as i32); }
v2[1][1] = 8;
println!("{:?}", v2); // [[0, 0], [1, 8], [2, 2], [3, 3]]

と言う感じで行けるようだ.
注意すべきなのは, Vec::with_capacity(10)というように初期化したり, v.reserve(10)というように領域を確保しても, 領域が保証されているだけで, そのまま値を入れようとすると実行時にエラーになってしまうので, 気をつけたい.

let mut c: Vec<i32> = Vec::with_capacity(10);
println!("{}", c.capacity()); // 10
println!("{}", c.len()); // 0
// c[2] = 8; // 実行時エラー


配列のループ

https://doc.rust-jp.rs/the-rust-programming-language-ja/1.6/book/loops.html

Rustのループは

for var in expression {
  code
}

という形式で, expressionの部分はイテレータを返す式にしなければいけないようだ. 逆にイテレータにすることでmax, minやfold, map, filter等の配列特有のメソッドを利用できるようになる. https://doc.rust-lang.org/std/iter/trait.Iterator.html https://qiita.com/lo48576/items/34887794c146042aebf1

arrayの場合は, array自体はiterableではないので何かしら付ける. arrayの大きさが32以下であれば, &[T]IntoIteratorと同様になる. それ以上の大きさであれば.iter(), .into_iter() を使う.

let a = [2, 3, 5, 7, 11];

for i in &a {
  print!("{} ", i); // 2 3 5 7 11 
}

for i in a.iter() {
  print!("{} ", i); // 2 3 5 7 11 
}

for i in a.into_iter() {
  print!("{} ", i); // 2 3 5 7 11 
}

元の配列をmutableにし, ループでもmutableな参照にすることで, 規則的に配列を変換することができる.

let mut p = [2, 3, 4];

for i in &mut p {
  *i *= 2;
}

println!("{:?}", p); // [4, 6, 8]

これはmapの方が楽なのでは?と思ったが, 調べるとRustのイテレータのmapは遅延評価なので, 使いにくいことがわかった.

let a = [2, 3, 4];
let b = a.iter().map(|x| x * 2);
println!("{:?}", b); // Map { iter: Iter([2, 3, 4]) }

// collect()で評価させる
let c: Vec<i32> = a.iter().map(|x| x * 2).collect();
println!("{:?}", c); // [4, 6, 8]

IntoIterator はsliceが使え, 既存のデータ構造の一部を安全に取り出して操作することができる.

let mut p = [0; 20];

for i in &mut p[2..10] {
  *i = 9;
}

println!("{:?}", p); // [0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]


vecはiterableなのでそのままループできてしまうのだが,

let mut v = vec![2, 3, 5, 7, 11];

for i in v {
  print!("{} ", i); // 2 3 5 7 11
}

これだと所有権がiに移ってしまってループの後でv.push(13);などが出来なくなってしまう. なので, 参照渡しを徹底するように心がけたい.

let mut v = vec![2, 3, 5, 7, 11];

for i in &v[2..4] {
  print!("{} ", i); // 5 7
}

v.push(13);

println!("{:?}", v); // [2, 3, 5, 7, 11, 13]