rs.nkmk.me

Rustで複数のVecを連結(append, extendなど)

Posted: | Tags: Rust, ベクタ

Rustで複数のベクタVecを連結(結合)して一つのVecにするには、appendextendメソッドを使う。

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

extendExtendトレイトのメソッド。VecにもExtendトレイトが実装されている。

Vecextendメソッドの引数に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を連結したい場合、appendextendを繰り返してもよいが、スライスのconcatメソッドやjoinメソッドを使う方法もある。

スライスのメソッドは型強制によって配列からも呼べるので、Vecの配列([v1, v2, ...])からconcatjoinを呼べばよい。appendextendのように既存の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]);

関連カテゴリー

関連記事