無効なポインターを生成する関数参照をキャストしますか?


9

私はサードパーティのコードのエラーを追跡しており、それを何かに沿って絞り込みました。

use libc::c_void;

pub unsafe fn foo() {}

fn main() {
    let ptr = &foo as *const _ as *const c_void;
    println!("{:x}", ptr as usize);
}

安定した1.38.0で実行すると、関数ポインタが出力されますが、ベータ版(1.39.0-beta.6)で毎晩「1」を返します。(遊び場

_推論されているものは何ですか?なぜ動作が変更されましたか?

これをキャストする正しい方法は単にであるとfoo as *const c_void思いますが、これは私のコードではありません。


「なぜ変更されたのか」にはお答えできませんが、最初はコードが間違っていることに同意します。fooはすでに関数ポインタであるため、アドレスを取得しないでください。これにより、サイズがゼロのタイプ(したがって魔法の値1)への二重参照が作成されます。
シェプマスター、

これは正確にあなたの質問に答えるわけではありませんが、おそらくあなたは望みます:let ptr = foo as *const fn() as *const c_void;
Peter Hall

回答:


3

この回答は、 、この質問が動機となったバグレポート

Rustの各関数には、個別の関数アイテムタイプがあります。これは、他のすべての関数の関数アイテムタイプとは異なります。このため、関数アイテムタイプのインスタンスは、情報をまったく格納する必要がありません。インスタンスが指す関数は、そのタイプから明らかです。変数xは

let x = foo;

サイズ0の変数です。

関数アイテム型は、必要に応じて暗黙的に関数ポインタ型に強制変換されます。変数

let x: fn() = foo;

は、シグネチャを持つ任意の関数への汎用ポインタfn()であるため、実際に指す関数へのポインタを格納する必要があるため、のサイズはxポインタのサイズになります。

関数のアドレスを取る場合&foo、実際にはサイズがゼロの一時値のアドレスを取ります。repo へのこのコミットのrust、サイズがゼロの一時ファイルがスタックに割り当てを作成するために使用され&foo、その割り当てのアドレスを返していました。このコミット以降、サイズがゼロのタイプは割り当てを作成せず、代わりにマジックアドレス1を使用します。これは、Rustの異なるバージョンの違いを説明しています。


これは理にかなっていますが、不安定な仮定に基づいて構築されているため、一般的に望ましい動作であるとは思いません。安全なRustコード内では、ZSTの値へのポインターを区別する理由はありません-コンパイル時に既知の値が1つしかないためです。これは、Rust型システムの外部(ここなど)でZST値を使用する必要がある場合に機能しなくなります。それはおそらくfnアイテムタイプと非キャプチャークロージャーにのみ影響し、それらには私の回答のように回避策がありますが、それでもまだかなりの足踏みです!
Peter Hall、

わかりました、Githubの問題に関する新しい応答を読んでいませんでした。私はそのコードでsegfaultを取得できますが、コードがsegfaultを引き起こす可能性がある場合、新しい動作は問題ないと思います。
Peter Hall、

すばらしい答えです。@PeterHall私は同じことを考えていました、そして私はまだこの問題について100%ですスタックレイアウトについては保証しますが、ZSTへのポインターの一意性は保証できません。これは、たとえば、私が理解しているように、ポインタのIDを保持することが保証されている*const i32へのキャストとは異なり*const c_voidます。
trentcl、

2

_推論されているものは何ですか?なぜ動作が変更されましたか?

生のポインタキャストを実行するたびに、変更できる情報は1つだけです(参照または生のポインタ、変更可能性、型)。したがって、このキャストを行う場合:

let ptr = &foo as *const _

参照から生のポインタに変更したので、推論される型は変更され_ ない必要があり、したがっての型になりますfoo。これは、関数では表現できない型ですfoo。。

そうする代わりに、関数ポインタに直接キャストできます。これは、Rust構文で表現できます。

let ptr = foo as *const fn() as *const c_void;

それが変わった理由については、それを言うのは難しい。ナイトリービルドのバグかもしれません。報告する価値はあります。たとえそれがバグでなくても、実際に何が起こっているかについてコンパイラチームから良い説明を受けるでしょう。



@MaciejGoszczycki報告ありがとうございます!回答は実際に私にとって明確なものでした。そこでの回答に基づいて回答を投稿します。
Sven Marnach
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.