注意: 最新版のドキュメントをご覧ください。この第1版ドキュメントは古くなっており、最新情報が反映されていません。リンク先のドキュメントが現在の Rust の最新のドキュメントです。
Rustの主たる魅力は、プログラムの動作についての強力で静的な保証です。
しかしながら、安全性検査は本来保守的なものです。
すなわち、実際には安全なのに、そのことがコンパイラには検証できないプログラムがいくらか存在します。
その類のプログラムを書くためには、制約を少し緩和するようコンパイラに対して伝えることが要ります。
そのために、Rustには unsafe
というキーワードがあります。
unsafe
を使ったコードは、普通のコードよりも制約が少なくなります。
まずシンタックスをみて、それからセマンティクスについて話しましょう。
unsafe
は4つの場面で使われます。
1つめは、関数がアンセーフであることを印付ける場合です。
unsafe fn danger_will_robinson() { // 恐ろしいもの }
たとえば、FFIから呼び出されるすべての関数はunsafe
で印付けることが必要です。
unsafe
の2つめの用途は、アンセーフブロックです。
unsafe { // 恐ろしいもの }
3つめは、アンセーフトレイトです。
fn main() { unsafe trait Scary { } }unsafe trait Scary { }
そして、4つめは、そのアンセーフトレイトを実装する場合です。
fn main() { unsafe trait Scary { } unsafe impl Scary for i32 {} }unsafe impl Scary for i32 {}
大きな問題を引き起こすバグがあるかもしれないコードを明示できるのは重要なことです。
もしRustのプログラムがセグメンテーション違反を起こしても、バグは unsafe
で印付けられた区間のどこかにあると確信できます。
Rustの文脈で、安全とは「どのようなアンセーフなこともしない」ことを意味します。
訳注: 正確には、安全とは「決して未定義動作を起こさない」ということです。 そして、安全性が保証されていないことを「アンセーフ」と呼びます。 つまり、未定義動作が起きるおそれがあるなら、それはアンセーフです。
知っておくべき重要なことに、たいていのコードにおいて望ましくないが、アンセーフ ではない とされている動作がいくらか存在するということがあります。
Rustはソフトウェアが抱えるすべての種類の問題を防げるわけではありません。 Rustでバグのあるコードを書くことはできますし、実際に書かれるでしょう。 これらの動作は良いことではありませんが、特にアンセーフだとは見なされません。
さらに、Rustにおいては、次のものは未定義動作で、 unsafe
コード中であっても、避ける必要があります。
訳注: 関数に付いている
unsafe
は「その関数の処理はアンセーフである」ということを表します。 その一方で、ブロックに付いているunsafe
は「ブロック中の個々の操作はアンセーフだが、全体としては安全な処理である」ということを表します。 避ける必要があるのは、未定義動作が起こりうる処理をアンセーフブロックの中に書くことです。 それは、アンセーフブロックの処理が安全であるために、その内部で未定義動作が決して起こらないことが必要だからです。 アンセーフ関数には安全性の保証が要らないので、未定義動作が起こりうるアンセーフ関数を定義することに問題はありません。
&mut T
と &T
は、 UnsafeCell<U>
を含む &T
を除き、LLVMのスコープ化された noalias モデルに従っています。アンセーフコードは、それら参照のエイリアシング保証を破ってはいけません。UnsafeCell<U>
を持たないイミュータブルな値・参照の変更std::ptr::offset
(offset
intrinsic) を使って、オブジェクトの範囲外を指すこと。ただし、オブジェクトの最後より1バイト後を指すことは許されている。std::ptr::copy_nonoverlapping_memory
(memcpy32
/memcpy64
intrinsics) を使うbool
における、 false
(0) か true
(1) でない値enum
の定義に含まれていない判別子char
における、サロゲートか char::MAX
を超えた値str
における、UTF-8でないバイト列アンセーフ関数・アンセーフブロックでは、Rustは普段できない3つのことをさせてくれます。たった3つです。それは、
以上です。
重要なのは、 unsafe
が、たとえば「借用チェッカをオフにする」といったことを行わないことです。
Rustのコードの適当な位置に unsafe
を加えてもセマンティクスは変わらず、何でもただ受理するようになるということにはなりません。
それでも、unsafe
はルールのいくつかを破るコードを書けるようにはするのです。
また、unsafe
キーワードは、Rust以外の言語とのインターフェースを書くときに遭遇するでしょう。
ライブラリの提供するメソッドの周りに、安全な、Rustネイティブのインターフェースを書くことが推奨されています。
これから、その基本的な3つの能力を順番に見ていきましょう。
static mut
のアクセスとアップデート。Rustには「static mut
」という、ミュータブルでグローバルな状態を実現する機能があります。
これを使うことはデータレースが起こるおそれがあるので、本質的に安全ではありません。
詳細は、この本のstaticセクションを参照してください。
生ポインタによって任意のポインタ演算が可能になりますが、いくつもの異なるメモリ安全とセキュリティの問題が起こるおそれがあります。 ある意味で、任意のポインタを参照外しする能力は行いうる操作のうち最も危険なもののひとつです。 詳細は、この本の生ポインタに関するセクションを参照してください。
この最後の能力は、unsafe
の両面とともに働きます。
すなわち、unsafe
で印付けられた関数は、アンセーフブロックの内部からのみ呼び出すことができます。
この能力は強力で多彩です。 Rustはいくらかのcompiler intrinsicsをアンセーフ関数として公開しており、また、いくつかのアンセーフ関数は安全性検査を回避することで、安全性とスピードを引き換えています。
繰り返しになりますが、アンセーフブロックと関数の内部で任意のことが できる としても、それをすべきだということを意味しません。コンパイラは、あなたが不変量を守っているかのように動作しますから、注意してください!