How to implement a Box-like `MyBox` that supports trait objects like `Vec<MyBox<dyn MyTrait>>`

huangapple go评论75阅读模式
英文:

How to implement a Box-like `MyBox` that supports trait objects like `Vec<MyBox<dyn MyTrait>>`

问题

以下是已翻译的内容:

以下的 MyBox 结构与 Box 设置非常相似,但在结构体 Vec<MyBox<dyn MyTrait>> 中它却无法正常工作。然而,在 Box 中的 trait 对象的向量却能够正常工作,即 Vec<Box<dyn MyTrait>>

错误信息是

期望的结构体是 mybox::MyBox<dyn mybox::MyTrait>
找到的结构体是 mybox::MyBox<mybox::MyStruct>

是什么让 Box 能够正确地进行类型检查?是 Box 实现了一个可以解决这个问题的 trait 吗?

use std::ptr::NonNull;

trait MyTrait {}

struct MyStruct {}

impl MyTrait for MyStruct {}

struct MyBox<T: ?Sized> {
    value: NonNull<T>
}

impl<T> MyBox<T> {
    pub fn new(value: T) -> MyBox<T> {
        unsafe {
            MyBox { value: NonNull::new_unchecked(Box::into_raw(Box::new(value))) }
        }
    }
}

/// 正常工作
fn main_box() {
    let mut v: Vec<Box<dyn MyTrait>> = Vec::new();
    let x: Box<MyStruct> = Box::new(MyStruct {});
    v.push(x);
}

/// 存在类型错误
fn main_mybox() {
    let mut v: Vec<MyBox<dyn MyTrait>> = Vec::new();
    let x: MyBox<MyStruct> = MyBox::new(MyStruct {});
    v.push(x);
}

我已经尝试模仿 Box<T> 的结构,但可能是我忽略了一个它实现的 trait。

英文:

The following MyBox struct is setup extremely similarly to Box, and yet it does not work with a vector of trait objects in the struct Vec&lt;MyBox&lt;dyn MyTrait&gt;&gt;. However a vector of trait objects in a Box works just fine, Vec&lt;Box&lt;dyn MyTrait&gt;&gt;.

The error is

> expected struct mybox::MyBox&lt;dyn mybox::MyTrait&gt;
> found struct mybox::MyBox&lt;mybox::MyStruct&gt;

What is it about Box that allows it to properly type check? Does Box implement a trait that allows it to resolve?

use std::ptr::NonNull;

trait MyTrait {}

struct MyStruct {}

impl MyTrait for MyStruct {}

struct MyBox&lt;T: ?Sized&gt; {
    value: NonNull&lt;T&gt;
}

impl&lt;T&gt; MyBox&lt;T&gt; {
    pub fn new(value: T) -&gt; MyBox&lt;T&gt; {
        unsafe {
            MyBox { value: NonNull::new_unchecked(Box::into_raw(Box::new(value))) }
        }
    }
}

/// Works just fine
fn main_box() {
    let mut v: Vec&lt;Box&lt;dyn MyTrait&gt;&gt; = Vec::new();
    let x: Box&lt;MyStruct&gt; = Box::new(MyStruct {});
    v.push(x);
}

/// Has type error
fn main_mybox() {
    let mut v: Vec&lt;MyBox&lt;dyn MyTrait&gt;&gt; = Vec::new();
    let x: MyBox&lt;MyStruct&gt; = MyBox::new(MyStruct {});
    v.push(x);
}

I have tried mimicking the structure of Box&lt;T&gt; but there must be a trait that it implements that I am missing.

答案1

得分: 1

MyStructdyn MyTrait 是不同的类型,允许它们之间的强制转换的是 CoerceUnsizedBox&lt;T, A&gt;Box&lt;U, A&gt; 的实现,但你缺少了这个实现。

#![feature(coerce_unsized, unsize)]
use std::marker::Unsize;
use std::ops::CoerceUnsized;
impl&lt;T, U&gt; CoerceUnsized&lt;MyBox&lt;U&gt;&gt; for MyBox&lt;T&gt;
where
    T: Unsize&lt;U&gt; + ?Sized,
    U: ?Sized {}

如你所见,目前需要两个功能来实现它,因此你必须使用夜间编译器来使用它。

如果你必须使用稳定的编译器,你可以在 Box 上执行强制转换,然后将其转换为 MyBox&lt;dyn MyTrait&gt;

impl&lt;T: ?Sized&gt; From&lt;Box&lt;T&gt;&gt; for MyBox&lt;T&gt; {
    fn from(b: Box&lt;T&gt;) -&gt; Self {
        unsafe {
            MyBox {
                value: NonNull::new_unchecked(Box::into_raw(b)),
            }
        }
    }
}
/// 存在类型错误
fn main_mybox() {
    let mut v: Vec&lt;MyBox&lt;dyn MyTrait&gt;&gt; = Vec::new();
    let b: Box&lt;dyn MyTrait&gt; = Box::new(MyStruct {}); // 强制转换发生在这里
    let x: MyBox&lt;dyn MyTrait&gt; = b.into();
    v.push(x);
}
英文:

MyStruct and dyn MyTrait are different types, what allows coercion from one to the other for a Box is the CoerceUnsized implementation of Box&lt;T, A&gt; to Box&lt;U, A&gt; which you're lacking.

#![feature(coerce_unsized, unsize)]
use std::marker::Unsize;
use std::ops::CoerceUnsized;
impl&lt;T, U&gt; CoerceUnsized&lt;MyBox&lt;U&gt;&gt; for MyBox&lt;T&gt;
where
    T: Unsize&lt;U&gt; + ?Sized,
    U: ?Sized {}

As you can see that requires 2 features to implement at the moment so you have to run a nightly compiler to use it.

If you have to use a stable compiler you could do the coercion on the Box and turn that into MyBox&lt;dyn MyTrait&gt; afterwards:

impl&lt;T: ?Sized&gt; From&lt;Box&lt;T&gt;&gt; for MyBox&lt;T&gt; {
    fn from(b: Box&lt;T&gt;) -&gt; Self {
        unsafe {
            MyBox {
                value: NonNull::new_unchecked(Box::into_raw(b)),
            }
        }
    }
}
/// Has type error
fn main_mybox() {
    let mut v: Vec&lt;MyBox&lt;dyn MyTrait&gt;&gt; = Vec::new();
    let b: Box&lt;dyn MyTrait&gt; = Box::new(MyStruct {}); // coercion happens here
    let x: MyBox&lt;dyn MyTrait&gt; = b.into();
    v.push(x);
}

huangapple
  • 本文由 发表于 2023年7月24日 01:03:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/76749427.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定