kdnakt blog

hello there.

Rust dojo第78回を開催した

第78回です。前回はこちら。

kdnakt.hatenablog.com

 

[第78回の様子]

2023/05/17に第78回を開催した。

内容としてはRust By Example 日本語版22. 安全でない操作の「22. 安全でない操作」、「22.1. Inline assembly」に取り組んだ。(22.1は途中まで...)
参加者は自分を入れて6人。ひさしぶりにドライバを担当した!

内容的にメモリ周りとか、アセンブリの話とかで、ちょうど今読んでいる本と近い内容なので興味深かった。

marabos.nl

[学んだこと]

  • 22. 安全でない操作
  • アンセーフ、つまり安全でない操作について学んでいく
    • ただし、アンセーフなコードは可能な限り小さくあるべき、と公式ドキュメントに書かれている
  • Rustでは、コンパイラのチェックを避けるためにunsafe { ... }のブロックを利用する
  • 用途は以下の通り
    • 安全でない関数の呼び出し(主にC言語などの外部関数呼出FFI
    • (その結果として得られる)生ポインタのデリファレンス(値取得)(なのでRustだけ使ってる場合は基本不要)
    • 静的なミュータブル変数へのアクセスや変更
    • 安全でないトレイトの実装
  • 生ポインタはアスタリスク(*)で表す。参照(&T)が必ず有効なデータを指しているのに対し、生ポインタは安全でないのでunsafeブロックでしかデリファレンスを実行できない
fn main() {
    let raw_p: *const u32 = &10;

    unsafe {
        // デリファレンスした結果を比較
        assert!(*raw_p == 10);
    }
}
use std::slice;

fn main() {
    // u32の配列
    let some_vector = vec![1, 2, 3, 4];

    let pointer = some_vector.as_ptr();
    let length = some_vector.len(); // 4

    unsafe {
        // ポインタの位置からu32の配列として4要素分読む
        let my_slice: &[u32] = slice::from_raw_parts(pointer, length);

        assert_eq!(some_vector.as_slice(), my_slice);
        println!("{:?}", my_slice); // [1, 2, 3, 4]
    }
}
  • from_raw_parts()は、ポインタが有効であること、ポインタの指す値の型が正しいことを前提に動作する
    • 型を意識せずに扱いたい場面などでポインタを直接取り回すことがあるらしい
    • Javaのコレクションでジェネリクスが使われて実行時に型情報が消えている話を思い出させてもらって、ぼんやりと理解できた
  • 呼び出す時の型を変えると、元の配列とは異なる値が得られる
use std::slice;

fn main() {
    // 元のベクターの値を変更
    let some_vector = vec![300, 2, 3, 4];

    let pointer: *const u32 = some_vector.as_ptr();
    let length = some_vector.len();

    unsafe {
        // ポインタの型を合わせる
        let pointer2: *const u8 = pointer as *const u8;
        // u8のスライスとして読み込む
        let my_slice: &[u8] = slice::from_raw_parts(pointer2, length);

        println!("{:?}", my_slice);
        // [44, 1, 0, 0]
        // 解説:u32型で32ビットで保持されている300の値を、u8(8ビット)×4のスライスとして読み込んでいる
        // 実行環境がリトルエンディアンだったので、桁が小さい数字が先頭にきている
        // 44 + 256 x 1 = 300で元の値と一致している
    }
}
  • 22.1. Inline assembly
  • Rustではasm!マクロを使って、アセンブリのコードを直接埋め込むことができる
    • パフォーマンスやタイミングが必要な場合にどうぞ、ということらしい
    • あるいはカーネルのコードなどハードウェアに近い部分でも必要とのこと
  • 基本的な使い方は以下の通り
use std::arch::asm;

unsafe {
    asm!("nop"); //何もしないアセンブリ命令
}
  • asm!マクロは必ずunsafeブロックで実行する必要がある:Rustの借用チェッカが保証しているものを壊すこともできてしまうので。

[まとめ]

モブプログラミングスタイルでRust dojoを開催した。
Rustでインラインアセンブリ書けるのは分かったけど、書きたい人いるんだろうか...。

今週のプルリクエストはこちら。

github.com

もうちょっとでRust by Exampleが終わるので、そしたら次は関数型言語とかやってみようかしら。