kdnakt blog

hello there.

Rust dojo第63回を開催した

新年一発目、第63回です。前回はこちら。

kdnakt.hatenablog.com

 

[第63回の様子]

2023/01/11に第63回を開催した。

内容としてはRust By Example 日本語版19. 標準ライブラリの型の「19.2. ベクタ型」と「19.3. 文字列」を途中まで取り組んだ。
参加者は自分を入れて6人くらい。久しぶりだったので自分がドライバを担当した。

今年もよろしくお願いします。

[学んだこと]

  • 19.2. ベクタ型
  • ベクタ型はサイズを変更可能な配列
    • メモリ上にベクタのために確保された領域を容量という
    • 要素を追加した時にベクタの容量を超える場合は、メモリが割り当て直される
  • 初期化の方法は2つ紹介されていた
// イテレータの要素を収集してベクタにする
let collected_iterator: Vec<i32> = (0..10).collect();
println!("Collected (0..10) into: {:?}", collected_iterator);
// Collected (0..10) into: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

// vec!マクロでも初期化可能
let mut xs = vec![1i32, 2, 3];
println!("Initial vector: {:?}", xs);
// Initial vector: [1, 2, 3]
  • 他にも初期化する方法はいくつかある
let mut vector: Vec<i32> = Vec::new();
vector.push(10);
vector.push(11);
println!("{:?}", vector);
// [10, 11]

// 同じ値でベクタを初期化する
let vector = [ 0; 32 ];
println!("{:?}", vector);
// [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
  • 要素を追加する場合はミュータブルなベクタにpush()する
let mut xs = vec![1i32, 2, 3];
xs.push(4);
println!("Vector: {:?}", xs);
// Vector: [1, 2, 3, 4]
  • ベクタの要素を取得するのはこんな感じ
let mut xs = vec![1i32, 2, 3];
xs.push(4);
println!("Second element: {}", xs[1]);
// Second element: 2

// 最後の要素をベクタから削除して取得するpop()
println!("Pop last element: {:?}", xs.pop());
// Pop last element: Some(4)

// 不正なインデックスの場合panicする
println!("Fourth element: {}", xs[3]);
// thread 'main' panicked at 'index out of bounds: the len is 3 but the index is 3', src/main.rs:39:36
  • panicが困る場合はget(index)を利用するとOption型で値を取得できる
let mut xs = vec![1i32, 2, 3];
println!("get position 0: {:?}", xs.get(0));
// get position 0: Some(3)

// 不正なインデックスの場合Noneが返る
println!("get position 3: {:?}", xs.get(3));
// get position 3: None
  • 順番に処理する場合は.iter()とかのメソッドを使う
let mut xs = vec![1i32, 2, 3];
for x in xs.iter() {
    println!("> {}", x);
}
// > 1
// > 2
// > 3

// インデックスつきでイテレート for (i, x) in xs.iter().enumerate() { println!("In position {} we have value {}", i, x); } // In position 0 we have value 1 // In position 1 we have value 2 // In position 2 we have value 3
// 値を変更しつつイテレート for x in xs.iter_mut() { *x *= 3; } println!("Updated vector: {:?}", xs); // Updated vector: [3, 6, 9]
  • 19.3. 文字列
  • Rustには文字列の型が2つある:Stringと&str
  • StringはUTF-8の配列としてVec<u8>でヒープ上に保持される
    • C言語と違って末端にnull文字を含まない
  • &strはUTF-8の配列のスライス(&[u8])
    • いつでもStringに変換可能
  • コード中に現れる"string"はリードオンリーなメモリ上に割り当てられた文字列への参照
    • {:p}でポインタを出力してみると、pangramのアドレスと、pangramの始まりのtheのアドレスが一致しているのがわかる
let pangram: &'static str = "the quick brown fox jumps over the lazy dog";
println!("Pangram ({:p}): {}", pangram, pangram);
// Pangram (0x55dcb9ca947c): the quick brown fox jumps over the lazy dog

// この場合、新しい文字列の割り当ては起こらない
for word in pangram.split_whitespace().rev() {
    println!("> {} {:p}", word, word);
}
// > dog (0x55dcb9ca94a4)
// > lazy (0x55dcb9ca949f)
// > the (0x55dcb9ca949b)
// > over (0x55dcb9ca9496)
// > jumps (0x55dcb9ca9490)
// > fox (0x55dcb9ca948c)
// > brown (0x55dcb9ca9486)
// > quick (0x55dcb9ca9480)
// > the (0x55dcb9ca947c)
  • 文字列とは直接関係ないけど、ベクタのdedup()メソッドは重複を排除してくれて便利そう
let pangram = "the quick brown fox jumps over the lazy dog";
let mut chars: Vec<char> = pangram.chars().collect();
chars.sort();
chars.dedup();
println!("chars: {:?}", chars);
// [' ', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']
// 重複したoとかが排除されている
  • 空のStringを作る場合は次のようにする
let mut string = String::new();
for c in chars {
    string.push(c);        // 文字を追加
    string.push_str(", "); // 文字列を追加
}
println!("{}", string);
//  , a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, 
  • 文字列をトリミングした場合は元の文字列のスライス=参照が返る
// ヒープに文字列を割り当てる
let string = String::from(" , a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, ");
let chars_to_trim: &[char] = &[' ', ','];
let trimmed_str: &str = string.trim_matches(chars_to_trim);
println!("Used characters: {} ", trimmed_str, trimmed_str);
// Used characters: a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z 
  • ヒープに割り当てた文字列に変更を加えると新しくメモリが確保される
let alice = String::from("I like dogs");
let bob: String = alice.replace("dog", "cat");

println!("Alice says: {} {:p}", alice, &alice);
// Alice says: I like dogs 0x7ffce90bf6a8

println!("Bob says: {} {:p}", bob, &bob);
// Bob says: I like cats 0x7ffce90bf6c0

 

[まとめ]

モブプログラミングスタイルでRust dojoを開催した。
スライスとか忘れちゃってるな...メモリが絡んでくるとやっぱり難しい。

今週のプルリクエストはこちら。ミスって2つに分かれてしまった...。

github.com

github.com