为什么不能借用盒装结构作为特征?

时间:2019-09-12 14:30:36

标签: rust traits

给出一个实现特征S的结构T,为什么Box<S>不实现Borrow<dyn T>

我希望编译的以下代码不会:

trait T{}
struct S{}
impl T for S{}

fn f1(s: &S) -> &dyn T {
    s
}

fn f2(s: &Box<S>) -> &dyn T {
    std::borrow::Borrow::borrow(s)
}

为什么f1不能编译,f2却可以编译? (从&S&dyn T的转换在第一种情况下完成,而不在第二种情况下完成)。

2 个答案:

答案 0 :(得分:3)

这与类型推断和类型强制工作的方式有关。 Borrow<B>特质的参数是借入值的类型,类型检查器需要知道它是什么。

如果您只写:

std::borrow::Borrow::borrow(s)

然后将从周围的代码中推断出B中的类型Borrow<B>。在您的情况下,将其推断为dyn T,因为这是返回值。但是,dyn T是与S完全不同的类型,因此不会进行类型检查。

一旦类型检查器知道返回的值是&S类型,则可以将其强制为&dyn T,但是您需要提供以下信息:

fn f2(s: &Box<S>) -> &dyn T {
    let s: &S = std::borrow::Borrow::borrow(s);
    s
}

或更简洁地说:

fn f2(s: &Box<S>) -> &dyn T {
    std::borrow::Borrow::<S>::borrow(s)
}

Sébastien Renauld's answer起作用的原因是,Deref使用关联的类型而不是类型参数。类型检查器可以轻松推断<S as Deref>::Target,因为每种类型只能有一个Deref的实现,并且关联的Target类型是唯一确定的。 Borrow有所不同,因为Box<S> 可以实施Borrow<()>Borrow<i32>Borrow<Box<Option<Vec<bool>>>>,...因此您必须更加明确关于您打算采用哪种实施方式。

答案 1 :(得分:1)

<div class="navbar-collapse collapse"> <ul class="nav navbar-nav"> @Html.ActivePage("User", "Index", "User") @Html.ActivePage("State", "Index", "State") @Html.ActivePage("City", "Index", "City") </ul> </div> 不直接等于&Box<S>,这就是为什么它不直接编译的原因。

您可以通过dereferencing相对容易地解决此问题,就像这样:

Box<&S>

(具有特征use std::ops::Deref; trait T{} struct S{} impl T for S{} fn f1(s : &S) -> &(dyn T) { s } fn f2(s : &Box<S>) -> &(dyn T) { s.deref() } 以便于阅读)

Deref的调用在deref()上进行,因此拥有&self就足以对其进行调用。它只返回&Box<S>,并且由于实现了&S类型的检出。