Rustのunsafeは安全じゃない

毎日がエブリデイ。最近Rustに入門しました。

Rustのunsafeは「下手するとメモリリークかSEGV起こすだけでしょ、間違ったロジック組めば実行時エラー出るだろうしヘーキヘーキ」的に思ってたけど、
それ以上に害悪になりうる挙動を見つけたのでメモ

安全とは何かについてはTaPLの一章に書いてあってなるほどと思ったのを記憶からざっくり引用すると
その言語が提供する抽象化を損なうか損なわないか、みたいなことらしい

以下、損なう例

# `std::mem::transmute`

同じサイズの型を無理やりキャストする便利な関数

バイト列をコピーせずに特定のデータ型に変換する用途で使うのがメジャーな使い方　の気がする

```rust
    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 リトルエンディアンなので
```
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=4bfea4d18ffc7b8f3d51bffbed61a846



メモリのレイアウトがわかっている場合、safeなコードでは冗長になったり、パフォーマンス的にデメリットが発生するような変換ももらくらく
（structをタプルに変換している↓）

```rust
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 }) };
}
```
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=72e94c7337a5d9fd946d78612dfd1ecd

使い方を間違えると、
（Sizedな型の参照は同じサイズ（64bitなら8, 32bitなら4）なことを利用して）

```rust
    println!("{}", unsafe { std::mem::transmute::<&[u8; 10000], &u8>(&[1; 10000]) } );
```
<img width="882" alt="スクリーンショット 2019-09-11 23.33.38.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265793/abb70d9c-54f3-363a-32da-dd49f1638b2b.png">

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

<img width="912" alt="スクリーンショット 2019-09-12 11.00.24-1.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265793/fae6a860-2baf-4a7e-49e2-73a80c4542a2.png">

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=a091b98bd3877560f0e5a286b75923f6
うまくやるとセグフォさせることができる

（この逆をやるとメモリリークするような気がするけど観測方法がわからないので今後の課題とする）

# Booleanの奇妙な話

ところでu8とboolはprimitive typeなのでasで相互に変換でき・・・

```rust
    println!("{}", true as u8); // => 1
    println!("{}", false as u8); // => 0
    println!("{}", 1u8 as bool); // error[E0054]: cannot cast as `bool`
```
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=92ca9c03be4853cc254623297892f4ce

なかった

boolは1byteだしu8も1byteなので

```rust
    println!("{}", std::mem::size_of::<bool>()); // => 1
    println!("{}", std::mem::size_of::<u8>()); // => 1
```
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=db977e504cf863c3d7832c27b820b22b

transmuteで無理やり変換してみる

<img width="1085" alt="スクリーンショット 2019-09-11 23.54.38.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265793/d198923f-39a3-4a7f-986a-9a2724cfba41.png">

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=06e490496dc6db8e859ab142bdc2dd69

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


!?

<img width="1044" alt="スクリーンショット 2019-09-11 23.56.11.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265793/89aaea06-b520-66d4-425b-de56a078c4a2.png">

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=ee50d0d9fc74b0a367c4aea2c652de4f

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

!?!?

<img width="1033" alt="スクリーンショット 2019-09-11 23.58.36.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265793/962e18ad-e8ca-0ef0-ba14-59abb37089c5.png">

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=cd748c1b5a497b00a810546946312ee0

trueとの比較を２回やると結果が変わったりする


ちなみに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.

https://doc.rust-lang.org/std/mem/fn.transmute.html