Rustで複数のVecを連結(append, extendなど)
Rustで複数のベクタVecを連結(結合)して一つのVecにするには、appendやextendメソッドを使う。
VecにVecを連結: append(引数に指定したVecは空になる)
appendメソッドは引数に指定したVecのすべての要素を呼び出し元のVecに追加する。引数に指定したVecは空になる。
いずれのVecもミュータブルでなければならない。
let mut v1 = vec![1, 2, 3];
let mut v2 = vec![4, 5, 6];
v1.append(&mut v2);
assert_eq!(v1, [1, 2, 3, 4, 5, 6]);
assert!(v2.is_empty());
VecにVecを連結: extend
extendはExtendトレイトのメソッド。VecにもExtendトレイトが実装されている。
Vecのextendメソッドの引数にVecを指定すると、すべての要素が呼び出し元のVecに追加される。引数に指定したVecの所有権は失われる。
let mut v1 = vec![1, 2, 3];
let v2 = vec![4, 5, 6];
v1.extend(v2);
assert_eq!(v1, [1, 2, 3, 4, 5, 6]);
// v2;
// error[E0382]: use of moved value: `v2`
所有権を失わせない例は後述。
Vecに配列やイテレータを連結
extendメソッドの引数にはIntoIteratorトレイトを実装した型の値を指定できる。
Vecだけでなく配列やスライスも指定可能。スライスだと一部の要素のみが追加される。
let mut v = vec![1, 2, 3];
let a = [4, 5, 6];
v.extend(a);
assert_eq!(v, [1, 2, 3, 4, 5, 6]);
let mut v1 = vec![1, 2, 3];
let v2 = vec![4, 5, 6];
v1.extend(&v2[1..]);
assert_eq!(v1, [1, 2, 3, 5, 6]);
イテレータをそのまま指定することもできる。mapなどで処理した結果がそのまま追加される。
let mut v1 = vec![1, 2, 3];
let v2 = vec![4, 5, 6];
v1.extend(v2.iter().map(|x| x * 10));
assert_eq!(v1, [1, 2, 3, 40, 50, 60]);
所有権を失いたくない場合
クローンを渡す
当然ながら、クローンを渡せば元のVecはそのまま。
let mut v1 = vec![1, 2, 3];
let v2 = vec![4, 5, 6];
v1.extend(v2.clone());
assert_eq!(v1, [1, 2, 3, 4, 5, 6]);
assert_eq!(v2, [4, 5, 6]);
要素がコピーできる型の場合、参照を渡す
要素がコピーできる型(Copyトレイトを実装している型)の場合のExtendトレイトも別に実装されている。
数値などのコピーできる型が要素の場合、参照を指定すると要素がコピーされる。
let mut v1 = vec![1, 2, 3];
let v2 = vec![4, 5, 6];
v1.extend(&v2);
assert_eq!(v1, [1, 2, 3, 4, 5, 6]);
assert_eq!(v2, [4, 5, 6]);
extend_from_sliceを使う
文字列Stringなどのコピーできない型が要素の場合は参照を指定するとエラーになるが、extend_from_sliceメソッドを使うと、要素がクローンされて呼び出し元に追加される。
引数はスライス&[T]。Vecの参照も指定可能。
let mut v1 = vec![String::from('a'), String::from('b'), String::from('c')];
let v2 = vec![String::from('d'), String::from('e'), String::from('f')];
// v1.extend(&v2);
// error[E0271]: type mismatch resolving ...
v1.extend_from_slice(&v2);
assert_eq!(v1, ["a", "b", "c", "d", "e", "f"]);
assert_eq!(v2, ["d", "e", "f"]);
配列の参照やスライスも指定できる。
let mut v = vec![String::from('a'), String::from('b'), String::from('c')];
let a = [String::from('d'), String::from('e'), String::from('f')];
v.extend_from_slice(&a);
assert_eq!(v, ["a", "b", "c", "d", "e", "f"]);
assert_eq!(a, ["d", "e", "f"]);
let mut v1 = vec![String::from('a'), String::from('b'), String::from('c')];
let v2 = vec![String::from('d'), String::from('e'), String::from('f')];
v1.extend_from_slice(&v2[1..]);
assert_eq!(v1, ["a", "b", "c", "e", "f"]);
assert_eq!(v2, ["d", "e", "f"]);
複数のVecを連結して新たなVecを生成: concat, join
複数のVecを連結したい場合、appendやextendを繰り返してもよいが、スライスのconcatメソッドやjoinメソッドを使う方法もある。
スライスのメソッドは型強制によって配列からも呼べるので、Vecの配列([v1, v2, ...])からconcatやjoinを呼べばよい。appendやextendのように既存のVecに追加されるのではなく、新たなVecが生成される。
なお、concat, joinは文字列のベクタVec<String>などを一つの文字列Stringに連結するのにも使われる。
concat
concatメソッドで複数のVecを連結して新たなVecを生成する例。
配列の要素に指定した時点で元のVecの所有権は失われる。
let v1 = vec![1, 2, 3];
let v2 = vec![4, 5, 6];
let v3 = vec![7, 8, 9];
let v = [v1, v2, v3].concat();
assert_eq!(v, [1, 2, 3, 4, 5, 6, 7, 8, 9]);
// v1;
// error[E0382]: use of moved value: `v1`
参照を指定するとエラーになるが、明示的にスライスを指定することは可能。この場合、元のVecはそのまま。
let v1 = vec![1, 2, 3];
let v2 = vec![4, 5, 6];
let v3 = vec![7, 8, 9];
// let v = [&v1, &v2, &v3].concat();
// error[E0599]: the method `concat` exists for array `[&std::vec::Vec<{integer}>; 3]`, but its trait bounds were not satisfied
let v = [&v1[..], &v2[..], &v3[..]].concat();
assert_eq!(v, [1, 2, 3, 4, 5, 6, 7, 8, 9]);
assert_eq!(v1, [1, 2, 3]);
assert_eq!(v2, [4, 5, 6]);
assert_eq!(v3, [7, 8, 9]);
let v = [&v1[1..], &v2[..], &v3[..2]].concat();
assert_eq!(v, [2, 3, 4, 5, 6, 7, 8]);
assert_eq!(v1, [1, 2, 3]);
assert_eq!(v2, [4, 5, 6]);
assert_eq!(v3, [7, 8, 9]);
concatの内部ではextend_from_sliceが使われている。
join
joinメソッドは引数に指定したセパレータを間に挿入する。セパレータには参照を指定する必要がある。
let v1 = vec![1, 2, 3];
let v2 = vec![4, 5, 6];
let v3 = vec![7, 8, 9];
// let v = [v1, v2, v3].join(100);
// error[E0277]: the trait bound `[std::vec::Vec<{integer}>]: std::slice::Join<{integer}>` is not satisfied
let v = [v1, v2, v3].join(&100);
assert_eq!(v, [1, 2, 3, 100, 4, 5, 6, 100, 7, 8, 9]);
セパレータに配列やVecを指定する場合は参照ではなく明示的にスライスを指定しなければならないので注意。
let v1 = vec![1, 2, 3];
let v2 = vec![4, 5, 6];
let v3 = vec![7, 8, 9];
// let v = [v1, v2, v3].join(&[100, 200]);
// error[E0277]: the trait bound `std::vec::Vec<{integer}>: std::borrow::Borrow<[[{integer}; 2]]>` is not satisfied
let v = [v1, v2, v3].join(&[100, 200][..]);
assert_eq!(v, [1, 2, 3, 100, 200, 4, 5, 6, 100, 200, 7, 8, 9]);
そのほかの使い方はconcatと同じ。
let v1 = vec![1, 2, 3];
let v2 = vec![4, 5, 6];
let v3 = vec![7, 8, 9];
let v = [&v1[1..], &v2[..], &v3[..2]].join(&100);
assert_eq!(v, [2, 3, 100, 4, 5, 6, 100, 7, 8]);
assert_eq!(v1, [1, 2, 3]);
assert_eq!(v2, [4, 5, 6]);
assert_eq!(v3, [7, 8, 9]);