Śledzę błąd w kodzie strony trzeciej i zawęziłem go do czegoś podobnego do.
use libc::c_void;
pub unsafe fn foo() {}
fn main() {
let ptr = &foo as *const _ as *const c_void;
println!("{:x}", ptr as usize);
}
Działa na stabilnej wersji 1.38.0, wypisuje wskaźnik funkcji, ale beta (1.39.0-beta.6) i nocne zwrócenie „1”. ( Plac zabaw )
Jakie są _
wnioski i dlaczego zachowanie się zmieniło?
Zakładam, że właściwy sposób na przesłanie tego byłoby po prostu foo as *const c_void
, ale to nie jest mój kod.
types
casting
rust
undefined-behavior
Maciej Goszczycki
źródło
źródło
foo
jest już wskaźnikiem funkcji, więc nie powinieneś do niego adresować. To tworzy podwójne odniesienie, pozornie do typu zerowego (a więc magicznej wartości1
).let ptr = foo as *const fn() as *const c_void;
Odpowiedzi:
Ta odpowiedź jest oparta na odpowiedziach na raport o błędzie motywowanych tym pytaniem .
Każda funkcja w Rust ma swój indywidualny typ elementu funkcji , który różni się od typu elementu funkcji każdej innej funkcji. Z tego powodu instancja typu elementu funkcji w ogóle nie musi przechowywać żadnych informacji - funkcja, na którą wskazuje, wynika z jego typu. Zatem zmienna x in
jest zmienną o rozmiarze 0.
Typy elementów funkcji domyślnie wymuszają, w razie potrzeby, typy wskaźników funkcji . Zmienna
jest ogólnym wskaźnikiem do dowolnej funkcji z podpisem
fn()
, a zatem musi przechowywać wskaźnik do funkcji, na którą faktycznie wskazuje, więc rozmiarx
to rozmiar wskaźnika.Jeśli weźmiesz adres funkcji,
&foo
faktycznie bierzesz adres tymczasowej wartości zerowej wielkości. Przed tym zatwierdzeniem dorust
repozytorium tymczasowe o zerowej wielkości zostały użyte do utworzenia przydziału na stosie i&foo
zwróciły adres tego przydziału. Ponieważ to zatwierdzenie, typy zerowe nie tworzą już przydziałów, zamiast tego używają magicznego adresu 1. To wyjaśnia różnicę między różnymi wersjami Rdzy.źródło
fn
typy przedmiotów i zamknięcia, których nie można przechwycić, a dla tych istnieje obejście, jak w mojej odpowiedzi, ale wciąż jest to strzał w dziesiątkę!*const i32
do*const c_void
którego, o ile rozumiem, nadal gwarantuje zachowanie tożsamości wskaźnika.Za każdym razem, gdy wykonujesz rzut surowym wskaźnikiem, możesz zmienić tylko jedną informację (wskaźnik lub wskaźnik surowy; zmienność; typ). Dlatego jeśli wykonasz tę obsadę:
ponieważ zmieniłeś odniesienie na surowy wskaźnik, typ wywnioskowany dla
_
musi być niezmieniony i dlatego jest typemfoo
, który jest jakimś niewyrażalnym typem dla funkcjifoo
.Zamiast tego możesz bezpośrednio rzutować na wskaźnik funkcji, który wyraża się w składni Rust:
Trudno powiedzieć, dlaczego tak się zmieniło. Może to być błąd w kompilacji nocnej. Warto to zgłosić - nawet jeśli nie jest to błąd, zespół kompilatora prawdopodobnie wyjaśni ci, co się naprawdę dzieje!
źródło