毎日がエブリデイ。最近Rustに入門しました。
Rustのunsafeは「下手するとメモリリークかSEGV起こすだけでしょ、間違ったロジック組めば実行時エラー出るだろうしヘーキヘーキ」的に思ってたけど、
それ以上に害悪になりうる挙動を見つけたのでメモ
安全とは何かについてはTaPLの一章に書いてあってなるほどと思ったのを記憶からざっくり引用すると
その言語が提供する抽象化を損なうか損なわないか、みたいなことらしい
以下、損なう例
std::mem::transmute
同じサイズの型を無理やりキャストする便利な関数
バイト列をコピーせずに特定のデータ型に変換する用途で使うのがメジャーな使い方 の気がする
let string = unsafe { std::mem::transmute::<&[u8], &str>(&[0x41, 0x42, 0x43]) };
println!("{}", string); // => ABC
let n = unsafe { std::mem::transmute::<[u8; 4], u32>([0,0,0,1]) };
println!("{}", n); // => 16777216 リトルエンディアンなので
メモリのレイアウトがわかっている場合、safeなコードでは冗長になったり、パフォーマンス的にデメリットが発生するような変換ももらくらく
(structをタプルに変換している↓)
struct A {
a: u8,
b: u8,
c: u8,
d: u8
}
fn main() {
let (i1, i2, i3, i4) = unsafe { std::mem::transmute::<A, (u8, u8, u8, u8)>(A { a: 82, b: 117, c: 115, d: 116 }) };
}
使い方を間違えると、
(Sizedな型の参照は同じサイズ(64bitなら8, 32bitなら4)なことを利用して)
println!("{}", unsafe { std::mem::transmute::<&[u8; 10000], &u8>(&[1; 10000]) } );

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=b4a28423b597e50567e7f9f95eed69e7
由来不明の値を出力させてみたり、

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=a091b98bd3877560f0e5a286b75923f6
うまくやるとセグフォさせることができる
(この逆をやるとメモリリークするような気がするけど観測方法がわからないので今後の課題とする)
Booleanの奇妙な話
ところでu8とboolはprimitive typeなのでasで相互に変換でき・・・
println!("{}", true as u8); // => 1
println!("{}", false as u8); // => 0
println!("{}", 1u8 as bool); // error[E0054]: cannot cast as `bool`
なかった
boolは1byteだしu8も1byteなので
println!("{}", std::mem::size_of::<bool>()); // => 1
println!("{}", std::mem::size_of::<u8>()); // => 1
transmuteで無理やり変換してみる

0はfalse、0以外はtrueっぽい
!?

trueと比較すると、
奇数(下位のビットが1)から生成されたboolはtrueを返し、
偶数から生成されたboolはfalseを返すっぽい
!?!?

trueとの比較を2回やると結果が変わったりする
ちなみにreleaseビルドかdebugビルドかによっても出力が変わる
- release: https://play.rust-lang.org/?version=stable&mode=release&edition=2018&gist=9cb5012b85cafa3a3c03226b7c18d3f1
- debug: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=9cb5012b85cafa3a3c03226b7c18d3f1
boolは2値であることや truthyなboolのオブジェクトは'true'と印字されること、みたいな抽象が損なわれていていかにも安全ではなさそうです
このようにunsafeなコードは落ちるだけでなく、合法っぽく見えても未定義動作を引き起こしうるので、全知全能でない人は蓋然性がない場合使わないようにしましょう
ここでRustのドキュメントから至言を引用すると、
transmute is incredibly unsafe. There are a vast number of ways to cause undefined behavior with this function. transmute should be the absolute last resort.