Bagaimana Anda mendapatkan alamat objek sifat? Saya mencoba ini:

fn func() {}

fn main() {
    let boxed_func: Box<dyn Fn()> = Box::new(func);

    println!("expect: {:p}", func as *const ()); // 0x55bb0207e570

    println!("actual: {:p}", &boxed_func); // 0x7ffe5217e5a0
    println!("actual: {:p}", Box::into_raw(boxed_func)); // 0x1
}

Tapi itu menghasilkan alamat yang berbeda.

2
Mas Bagol 23 April 2020, 06:34

1 menjawab

Jawaban Terbaik

Pertama, yang mudah: seperti yang disebutkan dalam komentar, &boxed_func hanyalah alamat variabel lokal boxed_func, bukan alamat data yang mendasarinya. Anggap saja sebagai penunjuk ke penunjuk.

Sekarang bagian yang sulit. Saya pikir satu hal yang membingungkan Anda adalah bahwa penunjuk ke objek ciri adalah petunjuk gemuk, dan ini tidak tercermin dengan mencetak dengan "{:p}". Mereka terdiri dari pointer ke data aktual serta pointer ke vtable, yang menyimpan informasi tentang implementasi sifat tersebut.

Ini dapat dilihat dengan kode (kemungkinan UB)

fn func() {}

fn main() {
    let boxed_func: Box<dyn Fn()> = Box::new(func);

    println!("function pointer: {:p}", func as fn()); // 0x560ae655d1a0
    println!("trait object data pointer: {:p}", boxed_func); // 0x1
    println!("stack pointer: {:p}", &boxed_func); // 0x7ffebe8f4688

    let raw = Box::into_raw(boxed_func);
    println!("raw data pointer: {:p}", raw); // 0x1

    // This is likely undefined behavior, since I believe the layout of trait objects isn't specified
    let more_raw = unsafe { std::mem::transmute::<_, (usize, usize)>(raw) };
    println!("full fat pointer: {:#x}, {:#x}", more_raw.0, more_raw.1); // 0x1, 0x560ae6789468
}

(tautan taman bermain)

Jadi pointer dasar sebenarnya dari boxed_func terdiri dari dua pointer: 0x1 dan 0x55ec289004c8 (hasilnya mungkin berbeda di sini). 0x1 adalah nilai biasa untuk pointer ke tipe berukuran nol. Jelas, Anda tidak ingin menggunakan pointer nol untuk tujuan ini, tetapi Anda juga tidak benar-benar membutuhkan pointer yang valid. Jenis berukuran nol sering dialokasikan menggunakan Unique::empty, yang hanya mengembalikan pointer yang menjuntai ke lokasi memori pada penyelarasan tipe (penyelarasan tipe berukuran nol adalah 1).

// Some zero-sized types and where they get allocated

struct Foo;

fn main() {
    let x = Box::new(());
    println!("{:p}", x); // 0x1

    let y = Box::new(Foo);
    println!("{:p}", y); // 0x1
}

(tautan taman bermain)

Jadi dalam situasi kita dengan objek sifat, ini memberi tahu kita bahwa bagian data dari objek sifat adalah (mungkin) tipe berukuran nol, yang masuk akal karena func tidak memiliki data apa pun yang terkait dengannya. dari apa yang diperlukan untuk menyebutnya. Informasi tersebut disimpan di vtable.


Cara yang lebih aman (lebih sedikit UB menginduksi) untuk melihat bagian mentah dari objek sifat adalah dengan struct khusus malam TraitObject.

#![feature(raw)]
use std::raw::TraitObject;

fn func() {}

fn main() {
    let boxed_func: Box<dyn Fn()> = Box::new(func);

    println!("function: {:p}", func as fn()); // 0x56334996e850
    println!("function trait object: {:p}", boxed_func); // 0x1
    println!("stack address: {:p}", &boxed_func); // 0x7ffee04c2378

    // Safety: `Box<dyn Trait>` is guaranteed to have the same layout as `TraitObject`.
    let trait_object = unsafe { std::mem::transmute::<_, TraitObject>(boxed_func) };
    println!("data pointer: {:p}", trait_object.data); // 0x1
    println!("vtable pointer: {:p}", trait_object.vtable); // 0x563349ba3068
}

(tautan taman bermain)

Cobalah ini dengan beberapa objek sifat lain dan lihat apakah Anda dapat menemukan beberapa yang tidak memiliki data berukuran nol.

5
SCappella 23 April 2020, 06:17