如何将一个带有盒式语法的特质转换为特质引用?

10
我有下面这段代码,试图从一个trait实例的盒子中获取一个trait对象的引用:
trait T {}

struct S {}

impl T for S {}

fn main() {
    let struct_box: Box<S> = Box::new(S {});
    let struct_ref: &S = &struct_box;

    let trait_box: Box<T> = Box::new(S {});
    let trait_ref: &T = &trait_box;
}

编译器返回以下错误:
error[E0277]: the trait bound `std::boxed::Box<T>: T` is not satisfied
  --> src/main.rs:12:25
   |
12 |     let trait_ref: &T = &trait_box;
   |                         ^^^^^^^^^^ the trait `T` is not implemented for `std::boxed::Box<T>`
   |
   = note: required for the cast to the object type `T`

我该如何正确地从 Box<T> 中借用 &T


2个回答

19

Box<T> 实现了 AsRef<T> trait,该 trait 提供了方法 as_ref(),因此您可以将其转换为引用:

let trait_ref: &T = trait_box.as_ref();

通常情况下,deref coercion 意味着你通常不需要明确地编写代码。如果你将类型为 Box<T> 的值传递给需要 &T 的函数,编译器会自动插入转换。如果你想调用一个需要 &selfT 方法,编译器会自动插入转换。然而,deref 强制转换不适用于到 trait 对象类型的强制转换(编译器将选择非精准大小转换,这就是你示例中发生的情况)。

你能进一步解释为什么在这种情况下选择了一个unsizing coercion而不是deref coercion吗?Rust参考手册的第10.7节“类型强制”没有给出解释。 - plafer

8
借用 Box 的内容,而不是 Box 本身:
let trait_ref: &T = &*trait_box;

涉及到 &S 的那一行代码之所以能够工作,是因为 Rust 通过“解引用强制转换”从 Box<S> 转换成 &S;也就是说,它会反复地解引用该值,直到类型匹配或无法再次解引用为止。
另一方面,对于进行强制转换为 trait 对象并不使用解引用;它会直接从给定的指针构造一个新的指针。如果无法这样做,则会失败。

我对解引用强制转换的理解是,编译器将调用deref()尽可能多次,直到类型匹配。在这里,trait_box.deref()确实会产生类型为dyn T的结果。我知道类型&dyn T是一个fat指针,它不是存储在Box数据结构中的内容。然而,编译器是否有足够的信息来为我构造这个fat指针呢?调用trait_box.deref()能够正常工作的事实表明它可以。那么,为什么你不希望这个过程被隐式地完成呢? - plafer

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