安全でない操作

この章の内容を見る前に、公式ドキュメントから引用した次の文章をお読みください。「コードベース中の、アンセーフな操作をするコードの量は、可能な限り小さく無くてはならない。」この戒めを頭に叩き込んだ上で、さあはじめましょう!Rustにおいて、アンセーフなブロックはコンパイラのチェックをスルーするために使われます。具体的には以下の4つの主要なユースケースがあります。

  • 生ポインタのデリファレンス
  • calling functions or methods which are unsafe (including calling a function over FFI, see a previous chapter of the book)
  • 静的なミュータブル変数へのアクセスや変更
  • 安全でないトレイトの実装

生ポインタ

生ポインタ*と参照&Tはよく似た機能を持ちますが、後者は必ず有効なデータを指していることが借用チェッカーによって保証されているので、常に安全です。生ポインタのデリファレンスはアンセーフなブロックでしか実行できません。

fn main() {
    let raw_p: *const u32 = &10;

    unsafe {
        assert!(*raw_p == 10);
    }
}

安全でない関数呼び出し

関数は unsafe として宣言できます。これはコンパイラの代わりにプログラマの責任で正しさを保証することを意味します。例として std::slice::from_raw_partsがあります。この関数は最初の要素へのポインタと長さを指定してスライスを作成します。

use std::slice;

fn main() {
    let some_vector = vec![1, 2, 3, 4];

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

    unsafe {
        let my_slice: &[u32] = slice::from_raw_parts(pointer, length);

        assert_eq!(some_vector.as_slice(), my_slice);
    }
}

For slice::from_raw_parts, one of the assumptions which must be upheld is that the pointer passed in points to valid memory and that the memory pointed to is of the correct type. If these invariants aren't upheld then the program's behaviour is undefined and there is no knowing what will happen.