Saya melacak kesalahan dalam kode pihak ketiga dan saya mempersempitnya menjadi sesuatu di sepanjang baris.
use libc::c_void;
pub unsafe fn foo() {}
fn main() {
let ptr = &foo as *const _ as *const c_void;
println!("{:x}", ptr as usize);
}
Berjalan di stabil 1.38.0 ini mencetak penunjuk fungsi, tetapi beta (1.39.0-beta.6) dan kembali setiap malam '1'. ( Taman bermain )
Apa yang _
disimpulkan dan mengapa perilaku berubah?
Saya berasumsi cara yang tepat untuk melakukan ini foo as *const c_void
, tapi ini bukan kode saya.
types
casting
rust
undefined-behavior
Maciej Goszczycki
sumber
sumber
foo
sudah menjadi penunjuk fungsi, jadi Anda tidak perlu mengambil alamatnya. Itu menciptakan referensi ganda, tampaknya untuk tipe berukuran nol (dengan demikian nilai ajaib1
).let ptr = foo as *const fn() as *const c_void;
Jawaban:
Jawaban ini didasarkan pada balasan pada laporan bug yang dimotivasi oleh pertanyaan ini .
Setiap fungsi di Rust memiliki jenis item fungsi masing-masing , yang berbeda dari jenis item fungsi dari setiap fungsi lainnya. Karena alasan ini, turunan dari jenis item fungsi tidak perlu menyimpan informasi sama sekali - fungsi apa yang ditunjukkannya jelas dari jenisnya. Jadi variabel x in
adalah variabel ukuran 0.
Tipe-tipe item fungsi secara implisit memaksa ke tipe-tipe pointer fungsi jika perlu. Variabel
adalah pointer umum ke fungsi apa pun dengan tanda tangan
fn()
, dan karenanya perlu menyimpan pointer ke fungsi yang sebenarnya ditunjuknya, sehingga ukurannyax
adalah ukuran pointer.Jika Anda mengambil alamat suatu fungsi
&foo
,, Anda sebenarnya mengambil alamat dengan nilai sementara berukuran nol. Sebelum ini berkomitmen untukrust
repo , temporaries berukuran nol digunakan untuk membuat alokasi pada stack, dan&foo
mengembalikan alamat alokasi itu. Karena komit ini, tipe berukuran nol tidak membuat alokasi lagi, dan alih-alih menggunakan alamat ajaib 1. Ini menjelaskan perbedaan antara versi Rust yang berbeda.sumber
fn
tipe item dan penutupan yang tidak menangkap dan bagi mereka yang ada solusinya, seperti dalam jawaban saya, tapi itu masih cukup mudah!*const i32
untuk*const c_void
yang, untuk pemahaman saya, masih dijamin untuk melestarikan identitas pointer.Setiap kali Anda melakukan lemparan pointer mentah, Anda hanya dapat mengubah satu informasi (referensi atau pointer mentah; mutabilitas; jenis). Karena itu, jika Anda melakukan ini:
karena Anda telah berubah dari referensi ke pointer mentah, tipe yang disimpulkan
_
harus tidak berubah dan karenanya adalah tipefoo
, yang merupakan tipe yang tidak dapat diekspresikan untuk fungsi tersebutfoo
.Alih-alih melakukan itu, Anda bisa langsung melemparkan ke pointer fungsi, yang dapat diekspresikan dalam sintaks Rust:
Adapun mengapa itu telah berubah, itu sulit dikatakan. Ini bisa jadi bug di build malam. Layak melaporkannya - bahkan jika itu bukan bug, Anda mungkin akan mendapatkan penjelasan yang bagus dari tim penyusun tentang apa yang sebenarnya terjadi!
sumber