Pls provide a manual implementation of the Trait object ....with a basic example with explanation
Given the following struct and trait:
struct MyStruct {
value: u8,
}
trait MyTrait {
fn print_value(&self);
}
impl MyTrait for MyStruct {
fn print_value(&self) {
println!("{}", self.value);
}
}
You can create a trait object for this trait by simply assigning a box to the trait object type:
fn main() {
let my_struct = Box::new(MyStruct {
value: 10,
});
// conversion from Box<MyStruct> to Box<dyn MyTrait> automatically inserted.
let trait_object: Box<dyn MyTrait> = my_struct;
trait_object.print_value();
}
The conversion is inserted automatically by the compiler when it detects that the types need a conversion to match.
If u don't mind Pls provide
- how vTable created for Mystruct
You can't really see the vtable — the compiler does it for you whenever you use the dyn MyTrait
type.
Sorry to irritating you..
Can u pls create a manual vtable
The layout of the vtable is an unspecified, unstable implementation detail. There's no supported way to create one “manually.” Vtables are only created by the compiler.
Ok is there any way to see the compiler expanding code
Well, you can simply disassemble the resulting executable, if you so wish (or, for example, use playground with "asm" output). This will not be very useful if you can't read the assembly, however.
Like expand macro
The vtable for a trait object will be similar to the following code:
// === DEFINE MyStruct ===
struct MyStruct {
value: u8,
}
impl MyStruct {
fn print_value(&self) {
println!("{}", self.value);
}
}
// === DEFINE CustomTraitObject
struct CustomVtable {
print_value: unsafe fn(*mut ()),
drop: unsafe fn(*mut ()),
}
struct CustomTraitObject {
value_ptr: *mut (),
vtable: &'static CustomVtable,
}
impl CustomTraitObject {
fn print_value(&self) {
unsafe {
(self.vtable.print_value)(self.value_ptr);
}
}
}
impl Drop for CustomTraitObject {
fn drop(&mut self) {
unsafe {
(self.vtable.drop)(self.value_ptr);
}
}
}
// === MAKE MyStruct COMPATIBLE WITH CustomTraitObject ===
unsafe fn my_struct_print_value(ptr: *mut ()) {
let ptr = ptr as *mut MyStruct;
(*ptr).print_value();
}
unsafe fn my_struct_drop(ptr: *mut ()) {
drop(Box::from_raw(ptr as *mut MyStruct));
}
fn into_trait_object(value: MyStruct) -> CustomTraitObject {
static VTABLE: CustomVtable = CustomVtable {
print_value: my_struct_print_value,
drop: my_struct_drop,
};
let boxed = Box::new(value);
let ptr = Box::into_raw(boxed) as *mut ();
CustomTraitObject {
value_ptr: ptr,
vtable: &VTABLE,
}
}
fn main() {
let my_struct = MyStruct {
value: 10,
};
let trait_object = into_trait_object(my_struct);
trait_object.print_value();
}
Trait objects are not expanded to Rust code. The highest-level thing you can see as their implementation is probably MIR.
Since the actual vtable is generated directly in the Rust compiler (not from generated code like a macro), there's no expanded code to view. But here is the compiler code that defines the types of entries in the vtable: rust/mod.rs at 2336406b38db20d1bad30d32914e73af1dd62742 · rust-lang/rust · GitHub
And the code that translates these items into (I think) LLVM IR: rust/meth.rs at 2336406b38db20d1bad30d32914e73af1dd62742 · rust-lang/rust · GitHub
Thank you " ALICE "!
If you want a generalization / a way to automate this, here is a PoC of a macro doing so:
#[apply(into_dyn!)]
trait Foo {
fn method<'slf> (&'slf mut self, arg: Arg)
-> String
;
fn bar (self)
-> ()
;
}
-
Demo
struct Implementor; impl Foo for Implementor { fn method (&mut self, arg: Arg) -> String { format!("{:p} {:?}", self, arg) } fn bar (self) {} } let foo: BoxDynFoo<'_> = Box::new(Implementor).into_dyn(); foo.method(Arg { x: 42 }); foo.bar();
-
Implementation
Expansion
// Re-emit, but with `IntoDyn` as a super-trait
trait Foo: FooIntoDyn {
fn method<'slf>(&'slf mut self, arg: Arg) -> String;
fn bar(self) -> ();
}
struct FooVTable {
method: for<'slf> unsafe fn(SelfPtr, Arg) -> String,
bar: unsafe fn(SelfPtr) -> (),
drop_boxed: unsafe fn(SelfPtr),
}
trait FooIntoDyn {
fn into_dyn<'slf>(self: Box<Self>) -> BoxDynFoo<'slf>
where
Self: 'slf + Sized;
}
use __helpers_Foo__::BoxDynFoo;
#[allow(nonstandard_style)]
mod __helpers_Foo__ {
use super::*;
impl<__Self: Foo> FooIntoDyn for __Self {
fn into_dyn<'slf>(self: Box<Self>) -> BoxDynFoo<'slf>
where
Self: 'slf + Sized,
{
trait __HasVTable: Foo + Sized {
const VTABLE: FooVTable = FooVTable {
method: |__self: SelfPtr, arg: Arg| unsafe {
<Self as Foo>::method(__self.cast::<Self>().as_mut(), arg)
},
bar: |__self: SelfPtr| unsafe {
<Self as Foo>::bar(*Box::<Self>::from_raw(
__self.cast::<Self>().as_ptr(),
))
},
drop_boxed: |__self| unsafe {
drop::<Box<Self>>(Box::from_raw(__self.cast::<Self>().as_ptr()))
},
};
}
impl<__Self: Foo> __HasVTable for __Self {}
unsafe {
BoxDynFoo {
ptr: mem::transmute(self),
vtable: &<Self as __HasVTable>::VTABLE,
}
}
}
}
pub struct BoxDynFoo<'lt> {
ptr: SelfPtr,
vtable: &'lt FooVTable,
}
impl Drop for BoxDynFoo<'_> {
fn drop(self: &'_ mut Self) {
unsafe { (self.vtable.drop_boxed)(self.ptr) }
}
}
impl Foo for BoxDynFoo<'_> {
#[inline]
fn method<'slf>(self: &'slf mut Self, arg: Arg) -> String {
unsafe { (self.vtable.method)(self.ptr, arg) }
}
#[inline]
fn bar(self: Self) -> () {
unsafe { (self.vtable.bar)(self.ptr) }
}
}
}
Real-life example
As several people have already mentioned, the exact layout for trait objects is deliberately left unspecified by the language so they can make changes without being locked into backwards compatibility.
A while ago I wrote an in-depth article on how you can create your own vtables and to do something similar to what rustc
does.
This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.