Как получить адрес объекта признака? - PullRequest
2 голосов
/ 23 апреля 2020

Как получить адрес объекта черты? Я пробовал это:

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
}

Но это дает разные адреса.

1 Ответ

5 голосов
/ 23 апреля 2020

Во-первых, самый простой: как упоминалось в комментариях, &boxed_func - это просто адрес локальной переменной boxed_func, а не адрес лежащих в основе данных. Думайте об этом как указатель на указатель.

Теперь сложная часть. Я думаю, что вас смущает то, что указатели на черты объектов жирные указатели , и это не отражается при печати с помощью "{:p}". Они состоят из указателя на фактические данные, а также указателя на виртуальную таблицу, в которой хранится информация о реализации черты.

Это можно увидеть с помощью (вероятно, 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
}

(ссылка на игровую площадку)

Таким образом, фактический базовый указатель boxed_func состоит из двух указателей: 0x1 и 0x55ec289004c8 (результаты могут отличаться здесь). 0x1 - обычное значение для указателей на типы нулевого размера. Очевидно, что вы не хотите использовать нулевой указатель для этой цели, но вам также не нужен действительный указатель. Типы нулевого размера часто выделяются с помощью Unique::empty, который просто возвращает висячий указатель на область памяти при выравнивании типа (выравнивание типа нулевого размера равно 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
}

(ссылка на игровую площадку)

Таким образом, в нашей ситуации с объектом признака это говорит нам о том, что часть данных объекта признака (вероятно) является нулевой размерный тип, что имеет смысл, поскольку func не имеет никаких данных, связанных с ним, кроме того, что необходимо для его вызова. Эта информация хранится в vtable.


Более безопасный (без UB-индуцирования) способ просмотра необработанных частей объекта черты - с ночной структурой 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
}

(ссылка на игровую площадку)

Попробуйте сделать это с другими объектами-чертами и посмотрите, сможете ли вы найти объекты, не имеющие данных нулевого размера.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...