Charaken 技術系ブログ

技術系に関して学んだことや、気になったことを記事にしていく。

【Rust】がばがばRust独学 - 8. Collections - 1 vector

f:id:charaken:20191223210559p:plain

Rustの公式ドキュメントを通して、Rustを独学していく記事になります。(がば要素あり)

doc.rust-lang.org

今回は、Collectionsの vector について学びつつ、気になった点をコード確認した結果を記事にしています。

  • versions
$ rustc --version
rustc 1.40.0 (73528e339 2019-12-16)


Common Collections

Collectionsと呼ばれるデータ構造が多数存在します。

  • vector :連結した値の可変で格納することが可能
  • string :charの集合
  • hash map :key, valueによるデータ構造

vectorVec<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です。