【Rust】がばがばRust独学 - 8. Collections - 1 vector
Rustの公式ドキュメントを通して、Rustを独学していく記事になります。(がば要素あり)
今回は、Collectionsの vector について学びつつ、気になった点をコード確認した結果を記事にしています。
- versions
$ rustc --version rustc 1.40.0 (73528e339 2019-12-16)
Common Collections
Collectionsと呼ばれるデータ構造が多数存在します。
vector ( Vec<T>
)
下記の公式ドキュメントを利用致します。
Storing Lists of Values with Vectors - The Rust Programming Language
vectorはownershipの原理より、スコープ外に出た場合にvectorがドロップされると、要素もドロップされます。
また、vectorは基本的に同一型のデータ構造となります。
実体化
新しい空のvectorの実体化は、下記のようになります。
fn main() { let v: Vec<usize> = Vec::new(); println!("{:#?}", v); }
[]
また、マクロ vec!
を利用した、初期値を持つvectorの生成は下記のようになります。
fn main() { let v: Vec<usize> = vec![1, 2, 3]; println!("{:#?}", v); }
[ 1, 2, 3, ]
更新
リストに対して追加を実施する場合は、 push
を利用します。この時、 v
は可変となるため、 mut
がついていることに注意してください。
fn main() { let mut v: Vec<usize> = vec![1, 2, 3]; v.push(4); println!("{:#?}", v); }
[ 1, 2, 3, 4, ]
vectorの要素を取得
単純に取得する場合は []
によって、配列番号を指定することで取得可能です。
fn main() { let v: Vec<usize> = vec![1, 2, 3]; let third: &usize = &v[2]; println!("third -> {:#?}", third); println!("v -> {:#?}", v); }
third -> 3 v -> [ 1, 2, 3, ]
しかし、上記はパニックになる原因となりえます。例えば、100番目の要素を読む場合の下記コードでは、パニックが起きていることがわかります。
fn main() { let v: Vec<usize> = vec![1, 2, 3]; let ng: &usize = &v[100]; println!("ng -> {:#?}", ng); println!("v -> {:#?}", v); }
thread 'main' panicked at 'index out of bounds: the len is 3 but the index is 100', /rustc/73528e339aae0f17a15ffa49a8ac608f50c6cf14/src/libcore/slice/mod.rs:2796:10 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
パニックを防ぐために、 get
関数を利用することにより、 Option<T>
として取得可能です。
fn main() { let v: Vec<usize> = vec![1, 2, 3]; let third: Option<&usize> = v.get(2); let ng: Option<&usize> = v.get(1000); println!("third -> {:#?}", third); println!("ng -> {:#?}", ng); println!("v -> {:#?}", v); }
third -> Some( 3, ) ng -> None v -> [ 1, 2, 3, ]
下記は公式ドキュメント(Storing Lists of Values with Vectors - The Rust Programming Language)の引用となりますが、クラッシュするよりも Option<T>
による制御の方がいいですよね。
That would be more user-friendly than crashing the program due to a typo!
可変・不変での注意点
同一スコープ内で可変と不変の参照を持つことが出来ないため、下記のように可変なvectorに対して不変なthirdによる参照後に可変処理 push
をすることによって、エラーとなります。
fn main() { let mut v: Vec<usize> = vec![1, 2, 3]; let third: Option<&usize> = v.get(2); v.push(4); println!("third -> {:#?}", third); println!("v -> {:#?}", v); }
error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable --> src/demo.rs:5:2 | 3 | let third: Option<&usize> = v.get(2); | - immutable borrow occurs here 4 | 5 | v.push(4); | ^^^^^^^^^ mutable borrow occurs here 6 | 7 | println!("third -> {:#?}", third); | ----- immutable borrow later used here
反復処理
for
により、 v
の参照 &v
から、 i
を利用して反復処理が可能です。
fn main() { let v: Vec<usize> = vec![1, 2, 3]; for i in &v { println!("{:#?}", i); } }
1 2 3
また、mutableな処理を実施したい場合は、i
の実体である *i
を利用して値にアクセスして利用可能です。公式ドキュメントのChapter 15にポインタの詳細が記載されているようです。
fn main() { let mut v: Vec<usize> = vec![1, 2, 3]; for i in &mut v { *i = *i + 10; } println!("v -> {:#?}", v); }
v -> [ 11, 12, 13, ]
enumの利用
vector自体は同一型でなければなりません。
fn main() { let v: Vec<usize> = vec![1, String::from("2")]; println!("v -> {:#?}", v); }
error[E0308]: mismatched types --> src/demo.rs:2:30 | 2 | let v: Vec<usize> = vec![1, String::from("2")]; | ^^^^^^^^^^^^^^^^^ expected usize, found struct `std::string::String` | = note: expected type `usize` found type `std::string::String`
そのため、別の型を利用する場合はenumにより実装可能です。
fn main() { #[derive(Debug)] enum SpreadsheetCell { Int(i32), Float(f64), Text(String), } let row = vec![ SpreadsheetCell::Int(3), SpreadsheetCell::Text(String::from("blue")), SpreadsheetCell::Float(10.12), ]; println!("row -> {:#?}", row); }
row -> [ Int( 3, ), Text( "blue", ), Float( 10.12, ), ]
最後に
Collectionsのvectorは便利ですね。 get
が有能で。。。
他言語でも、単純にloopを回す程度であればあまり気にせず回せばOKなのですが、直接要素を触るパターンもしばしばあるため、チェックパターン(length, is_nullなど)を入れなければならないことが多いので。。。
次は、Collectionsのstringです。