Ich habe einen Fehler im Code von Drittanbietern aufgespürt und ihn auf etwas in der Art von eingegrenzt.
use libc::c_void;
pub unsafe fn foo() {}
fn main() {
let ptr = &foo as *const _ as *const c_void;
println!("{:x}", ptr as usize);
}
Bei stabiler Version 1.38.0 wird der Funktionszeiger gedruckt, aber Beta (1.39.0-Beta.6) und nächtliche Rückgabe '1'. ( Spielplatz )
Worauf wird geschlossen _
und warum hat sich das Verhalten geändert?
Ich gehe davon aus, dass der richtige Weg, dies zu übertragen, einfach wäre foo as *const c_void
, aber dies ist nicht mein Code.
types
casting
rust
undefined-behavior
Maciej Goszczycki
quelle
quelle
foo
ist bereits ein Funktionszeiger, daher sollten Sie ihm keine Adresse geben. Dadurch entsteht eine doppelte Referenz, die scheinbar auf einen Typ mit der Größe Null (also den magischen Wert1
) verweist .let ptr = foo as *const fn() as *const c_void;
Antworten:
Diese Antwort basiert auf den Antworten auf den durch diese Frage motivierten Fehlerbericht .
Jede Funktion in Rust hat ihren eigenen Funktionselementtyp , der sich vom Funktionselementtyp jeder anderen Funktion unterscheidet. Aus diesem Grund muss eine Instanz des Funktionselementtyps überhaupt keine Informationen speichern. Auf welche Funktion sie verweist, geht aus ihrem Typ hervor. Also die Variable x in
ist eine Variable der Größe 0.
Funktionselementtypen werden bei Bedarf implizit zu Funktionszeigertypen gezwungen . Die Variable
ist ein generischer Zeiger auf eine Funktion mit Signatur
fn()
und muss daher einen Zeiger auf die Funktion speichern, auf die er tatsächlich zeigt, sodass die Größe vonx
die Größe eines Zeigers ist.Wenn Sie die Adresse einer Funktion verwenden, nehmen
&foo
Sie tatsächlich die Adresse eines temporären Werts mit der Größe Null. Vor dieser Festschreibung für dasrust
Repo wurden temporäre Dateien mit der Größe Null verwendet, um eine Zuordnung auf dem Stapel zu erstellen, und&foo
die Adresse dieser Zuordnung zurückgegeben. Seit diesem Commit erstellen Typen mit der Größe Null keine Zuordnungen mehr und verwenden stattdessen die magische Adresse 1. Dies erklärt den Unterschied zwischen den verschiedenen Versionen von Rust.quelle
fn
Artikeltypen und nicht erfassende Verschlüsse, und für diese gibt es eine Problemumgehung, wie in meiner Antwort, aber es ist immer noch eine ziemliche Fußwaffe!*const i32
, bei*const c_void
dem nach meinem Verständnis die Identität des Zeigers garantiert erhalten bleibt.Jedes Mal, wenn Sie eine Rohzeigerumwandlung durchführen, können Sie nur eine Information ändern (Referenz oder Rohzeiger; Veränderlichkeit; Typ). Wenn Sie diese Besetzung machen:
da man von einem Verweis auf einen Rohzeiger geändert haben, abgeleitet der Typ für
_
müssen unverändert sein und ist daher die Art derfoo
, die für die Funktion einige unaussprechliche Typ istfoo
.Stattdessen können Sie direkt in einen Funktionszeiger umwandeln, der in der Rust-Syntax ausgedrückt werden kann:
Warum es sich geändert hat, ist schwer zu sagen. Es könnte ein Fehler im nächtlichen Build sein. Es lohnt sich, dies zu melden - auch wenn es sich nicht um einen Fehler handelt, erhalten Sie vom Compilerteam wahrscheinlich eine gute Erklärung darüber, was tatsächlich vor sich geht!
quelle