diff --git a/src/transmutes.md b/src/transmutes.md index 67ae621..f3e6f81 100644 --- a/src/transmutes.md +++ b/src/transmutes.md @@ -7,72 +7,108 @@ 型システムから抜け出しましょう! 何がなんでもビットを再解釈します! この本は アンセーフなもの全てについて書かれていますが、この章でカバーされている操作を やるよりも、他の方法を見つけるよう深刻に考えるべきだということは、 -いくら強調しようとも、強調しきれません。これは本当に、マジで、 Rust で出来る +いくら強調しようとも、十分には強調しきれません。これは本当に、マジで、 Rust で出来る 最も恐ろしいアンセーフなことです。ここではガードレールは爪楊枝のように脆いです。 -`mem::transmute` は型 `T` の値を受け取り、その値が型 `U` であると再解釈します。 +[`mem::transmute`][transmute] は型 `T` の値を受け取り、その値が型 `U` であると再解釈します。 唯一の制約は、 `T` と `U` が同じサイズを持つとされていることです。 この操作によって未定義動作が起こる方法を考えると、気が遠くなります。 * まず真っ先に、*いかなる*型においても、無効状態のインスタンスを作ることは、本当に予測不可能な混沌状態を引き起こすでしょう。 + `3` を `bool` にトランスミュートしないでください。 + たとえその `bool` に対してまったく何も*しない*としてもです。 + とにかくやめてください。 * transmute はオーバーロードされたリターン型を持ちます。もしリターン型を指定しなかった場合、 推論を満たす、びっくりするような型を生成するかもしれません。 -* 無効なプリミティブを生成することは未定義動作を引き起こします。 -* repr(C) でない型の間でのトランスミュートは未定義動作を引き起こします。 -* & から &mut へのトランスミュートは未定義動作を引き起こします。 - * & から &mut へのトランスミュートは*いつも*未定義動作を引き起こします。 - * いいえ、これは出来ません。 - * いいか、君は特別じゃないんだ。 +* `&` から `&mut` へのトランスミュートは未定義動作を引き起こします。 + 特定の使い方は安全であるように*見える*かもしれませんが、Rust のオプティマイザは共有参照がそのライフタイムを通して変更されないことを自由に仮定することができることから、このようなトランスミュートはその仮定に抵触することに注意する必要があります。 + よって、 + * `&` から `&mut` へのトランスミュートは*いつも*未定義動作を引き起こします。 + * いいえ、これは出来ません。 + * いいか、君は特別じゃないんだ。 * 明確にライフタイムが指定されていない参照へのトランスミュートは[無制限のライフタイム]を生成します。 +* 異なる複合型の間でトランスミュートを行う場合、それらが同じように配置されていることを確かめる必要があります! + もしレイアウトが異なっているなら、間違ったフィールドが間違ったデータによって埋められることとなり、あなたを不幸にさせ、未定義動作を引き起こす可能性もあります(上記を見てください)。 + + それでは、レイアウトが同じかどうかはどのように分かるのでしょうか? + `repr(C)` 型と `repr(transparent)` 型については、レイアウトは正確に定義されています。 + しかし、ありふれた `repr(Rust)` についてはそうではありません。 + 同じジェネリック型の異なるインスタンスさえもまったく異なるレイアウトを持ちえます。 + `Vec` と `Vec` はフィールドを同じ順序で持つ*かもしれません*し、そうならないかもしれません。 + データレイアウトに関して、何が正確に保証されていて、何が保証されていないかについての詳細な説明は、まだ[UCG WG][ucg-layout]で作業が進められています。 -`mem::transmute_copy` は、どうにかして transmute よりも*本当に更に*アンセーフな事をしようとします。 +[`mem::transmute_copy`][transmute_copy] は、どうにかして transmute よりも*本当に更に*アンセーフな事をしようとします。 この関数は `&T` から `size_of` バイトコピーし、これらを `U` として解釈します。 もし `U` が `T` よりも大きい場合、未定義動作を引き起こしますが、 `mem::transmute` の サイズチェックはなくなっています ( `T` の先頭部分をコピーすることが有効である場合があるためです) 。 -そしてもちろん、これらの関数の機能のほとんどを、ポインタのキャストを利用することで -得ることができます。 - +そしてもちろん、これらの関数の機能のすべてを、生ポインタのキャストまたは `union` を利用することで得ることができますが、そこには lint やその他の基本的な健全性チェックは一切ありません。 +生ポインタのキャストや `union` は、上で述べたルールを魔法のように回避してくれるわけではありません。 -[無制限のライフタイム]: unbounded-lifetimes.html +[無制限のライフタイム]: ./unbounded-lifetimes.md +[transmute]: ../std/mem/fn.transmute.html +[transmute_copy]: ../std/mem/fn.transmute_copy.html +[ucg-layout]: https://rust-lang.github.io/unsafe-code-guidelines/layout.html