允许使用用户定义的类型创建 trait 对象?

3
在 Rust 中,除了引用之外,Box<T>Rc<T>Arc<T> 也允许创建 trait 对象(例如,从 Box<Type>Box<dyn Trait>)。但是否有一种方法可以使用用户定义的通用“智能指针”类型进行相同的转换呢?
例如,MyBox<T> 是围绕 Box<T> 的薄包装器,但以下代码会导致编译错误:
use std::io::Write;

pub fn main() {
    let std_box: Box<Vec<u8>> = Box::new(Vec::new());
    let std_dyn: Box<dyn Write> = std_box;
    // ^ this conversion is allowed.

    let my_box: MyBox<Vec<u8>> = MyBox { t: Box::new(Vec::new()) };
    let my_dyn: MyBox<dyn Write> = my_box;
    // ^ this conversion is not allowed.
}

struct MyBox<T: ?Sized> {
    t: Box<T>,
}

error[E0308]: mismatched types
 --> traits/src/trait_objs.rs:7:36
  |
7 |     let my_dyn: MyBox<dyn Write> = my_box;
  |                 ----------------   ^^^^^^ expected trait object `dyn std::io::Write`, found struct `Vec`
  |                 |
  |                 expected due to this
  |
  = note: expected struct `MyBox<dyn std::io::Write>`
             found struct `MyBox<Vec<u8>>`
1个回答

5
很遗憾,对于稳定版的Rust来说,“你不行”是答案。Rust在允许哪些隐式强制类型转换方面非常保守。特别是从文档中我们可以看到适用于Box的规则。
引用如下:

以下类型之间允许进行强制类型转换:

...

  • TyCtor(T) 到 TyCtor(U),其中TyCtor(T) 可以是以下之一

    • &T
    • &mut T
    • *const T
    • *mut T
    • Box<T>

并且通过非固定大小强制转换可以得到U

对应的非固定大小强制转换规则如下:
  • T实现了U + SizedU对象安全的时,T可以强制转换为dyn U
在目前的Rust版本中并没有太多特殊情况需要考虑。
然而,如果你愿意使用仅限于Nightly版的特性,那么你就可以使用令人兴奋的CoerceUnsized trait,其预期用例是...类似于Box这样的智能指针会进行类型转换。
#![feature(unsize, coerce_unsized)]

use std::ops::CoerceUnsized;
use std::marker::Unsize;

impl<T, U> CoerceUnsized<MyBox<U>> for MyBox<T> where T: Unsize<U>, U: ?Sized {}

这告诉 Rust 我们可以将 MyBox<T> 转换为 MyBox<U>,如果 TU 在“基本上相同”的情况下,对于适当的“基本上相同”的定义。不需要在实现上定义任何函数;trait impl 只需要存在即可。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接