🦀

The Rust Programing Language 6日目

2023/11/04に公開

前回のあらすじ
モジュールの分割方法、利用方法を学んだ
この辺結構感覚でやってしまってるので良く無いなあと反省

本日は8章 一般的なコレクション

本日の学び

ベクタ

  • これ競プロの解説とか読んでるとC++の実装でよく見る気がする
    • リストみたいなもんかなと思ってた
  • let v: Vec<i32> = Vec::new();
    • newするときは型注釈をつける
  • vec!マクロで値つきで生成可能
    • let v = vec![1, 2, 3];
  • 値の追加はpush
    • 当然mutにする必要がある
  • ベクタもスコープを抜ければ解放される
    • 中身の要素も解放される
  • ベクタの要素を読む
        let v = vec![1, 2, 3, 4, 5];
    
        let third: &i32 = &v[2];
        println!("The third element is {}", third);
    
        match v.get(2) {
    	//                      "3つ目の要素は{}です"
    	Some(third) => println!("The third element is {}", third),
    	//               "3つ目の要素はありません。"
    	None => println!("There is no third element."),
        }
    
    • 要素を得る方法は2つある
      • &v[2]として要素の参照を取る
        • 無い添字を使うとパニックする
      • v.get(2)として、Option<&T>を取る
        • 無い添字を使ってもパニックせず、Noneが返る
  • 要素が不変として借用されている時、要素を終端に追加することもできない
    • 要素を追加するとき、連続したヒープ領域を確保できない際は新しいスペースへ移動するため
  • for i in &vでループできる
        let mut v = vec![100, 32, 57];
        for i in &mut v {
    	*i += 50;
        }
    
    • 可変参照が参照している値を変更するためには、参照外し演算子*を使用する必要がある
      • よくわからないけど15章で追加解説があるらしいのでスルー
  • Enumを経由して複数の肩を保持することができる
    • 確かに〜
  • 公式APIドキュメントを読もう!

文字列

  • Rustには言語の核としては1種類しか文字列型が存在しない
    • 文字列スライスtの&str
    • String型は、言語の核としてではなく標準ライブラリで提供される
    • 標準ライブラリには他の文字列型も含まれる
      • OsString, OsStr, CString, CStr etc..
  • 文字列生成
    • let mut s = String::new();
      • 空の文字列を生成する
        • あんまり使うことなさそう
    • 文字列の初期値となるデータをto_stringメソッドで変換
      let data = "initial contents";
      let s = data.to_string();
      
      // the method also works on a literal directly:
      let s = "initial contents".to_string();
      
    • let s = String::from("initial contents");でも同じ
      • こっちのが楽なのでこっち使うのかな?
  • 文字列の連結
    • +演算子による連結
      let s1 = String::from("Hello, ");
      let s2 = String::from("world!");
      let s3 = s1 + &s2; // s1はムーブされ、もう使用できないことに注意
      
      • +演算子はaddメソッドを使用する
        • fn add(self, s: &str) -> String {
        • 標準ライブラリではaddはジェネリクスで定義されている
        • これはString用のシグニチャ
        • 上記の通りStringに追加できるのは&strのみだが、&Stringを渡している
          • コンパイラが&strに型強制している
        • selfは所有権をもらうので、後続で使用できないことに注意する
      • format!マクロを使うと見やすく、所有権も奪わない
  • Rustでは文字列に添字でアクセスできない
    • 理由はいくつかあるが、簡単に言うと想定外のバグを防ぐため、パフォーマンスのためという感じっぽいです(諦め)
    • スライスはできるが、パニックさせることが起こりやすいので注意する
      • あまりしない方が良さそうだけど、文字列の切り出しはどうするのだろう
        • クレート使うことになりそう

ハッシュマップ

  • HashMapは初期化処理でスコープに入らないので、useする必要がある
  • 生成マクロも存在しない
  • タプルのベクタに対してcollectメソッドを使って生成できる
    use std::collections::HashMap;
    
    let teams  = vec![String::from("Blue"), String::from("Yellow")];
    let initial_scores = vec![10, 50];
    
    let scores: HashMap<_, _> = teams.iter().zip(initial_scores.iter()).collect();
    
    • collectは色々なデータ構造にまとめられるので、HashMap<_, _>として指定する
      • ベクタの中身からデータ型については推論できる
  • insertでmapに挿入すると、所有権はムーブする
  • get(Key)で値を取り出せる
    • Option<&V>を返す
  • for (key, value) in &scores {の様にループできる
  • 同様のkeyをinsertすると、値は上書きされる
  • entry APIを使って、「まだ無ければ挿入」ができる
    use std::collections::HashMap;
    
    let mut scores = HashMap::new();
    scores.insert(String::from("Blue"), 10);
    
    scores.entry(String::from("Yellow")).or_insert(50);
    scores.entry(String::from("Blue")).or_insert(50);
    
    println!("{:?}", scores);
    
  • 元の値を利用して値を更新する
    use std::collections::HashMap;
    
    let text = "hello world wonderful world";
    
    let mut map = HashMap::new();
    
    for word in text.split_whitespace() {
        let count = map.entry(word).or_insert(0);
        *count += 1;
    }
    
    println!("{:?}", map);
    
    • or_insertは値への可変参照を返すので、参照外しして更新できる
  • 標準のHashMapはパフォーマンスを落として安全性を得ている
    • 気に食わなければ外部クレートを使えば良い

本日のまとめ

ベクタ、まあリストだなと思ったけどちょいちょい考えないといけないことが多そうな感じがした
HashMapについては結構すんなり使えそう
文字列の扱いで困ること今後ありそうだなーと強く思ったので、楽しみにしておく

Discussion