Как получить v-ptr для данной комбинации Trait/Struct?

В Русте &T где T это trait это жирная ссылка, которая на самом деле соответствует raw::TraitObject:

pub struct TraitObject {
    pub data: *mut (),
    pub vtable: *mut (),
}

С помощью TraitObject можно деконструировать и реконструировать &T на досуге.

Однако при получении vtable от разрушения &T легко, что если у меня никогда не будет &T во-первых, но просто T а также S; по сути, что-то вроде:

fn make_vptr<T: ?Sized, S>() -> *mut ();

Как я мог угадать V-PTR оттуда? Есть ли что-то, что я мог бы использовать?

Примечание: наивная реализация создания S (или колдовать его из воздуха), а затем сделать &T ссылка не работает; компилятор жалуется, что T не обязательно trait и поэтому &T или один указатель или два указателя в размере.

2 ответа

Решение

Можно использовать макрос для магической работы:

#![feature(raw)]

macro_rules! make_vptr(
    ($S:ty, $T:ty) => ({
        let s: &$S = unsafe { ::std::mem::uninitialized() };
        let t: &$T = s;
        let r: ::std::raw::TraitObject = unsafe { ::std::mem::transmute(t) };
        r.vtable
    })
);

Этот код не скомпилируется, если T это не черта (спасибо transmute(..) проверяя это &T жирный указатель) или если T не реализуется S (благодаря назначению).

Затем его можно использовать напрямую:

use std::fmt::Display;

fn main() {
    let u32_display_vtable = make_vptr!(u32, Display);

    let x = 42u32;

    let disp: &Display = unsafe {
        ::std::mem::transmute(::std::raw::TraitObject {
            data: &x as *const _ as *mut _,
            vtable: u32_display_vtable,
        })
    };

    println!("{}", disp);
}

Я не верю, что это возможно в настоящее время.

Для того чтобы это работало, вы должны быть в состоянии ограничить T универсальный параметр, чтобы принимать только черты. Вы не можете сделать это. В результате он никогда не позволит вам ничего сделать с &T это зависит от того, является ли это чертой, такой как получение vtable.

Другие вопросы по тегам