3

例如:

trait TraitX { }
trait TraitY { }
impl TraitX for TraitY { }

我想这与

impl<A: TraitY> TraitX for A { }

但错误消息另有说明:

$ rustc --version
rustc 0.12.0-nightly (a70a0374e 2014-10-01 21:27:19 +0000)
$ rustc test.rs
test.rs:3:17: 3:23 error: explicit lifetime bound required
test.rs:3 impl TraitX for TraitY { }
                          ^~~~~~

在 Rust 中(或它impl TraitX for TraitY的某些具有显式生命周期的变体)是否意味着什么?如果是这样,它的使用示例是什么?

4

2 回答 2

4

impl TraitX for TraitYTraitY用作动态大小的类型 (DST)。如果我们添加所需的生命周期限制(有关生命周期限制的必要性的更多信息,请参见例如this),编译器将以这种方式抱怨:

trait TraitX { }
trait TraitY { }
impl<'a> TraitX for TraitY+'a { }

fn main() {}

<anon>:3:1: 3:34 error: the trait `core::kinds::Sized` is not implemented for the type `TraitY+'a`
<anon>:3 impl<'a> TraitX for TraitY+'a { }
         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<anon>:3:1: 3:34 note: the trait `core::kinds::Sized` must be implemented because it is required by `TraitX`
<anon>:3 impl<'a> TraitX for TraitY+'a { }
         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

围栏

错误是说TraitY+'a没有大小,也就是说,它在编译时没有已知的大小(例如u8,大小为 1,Vec<T>是 3 个指针的大小)。

该语法是TraitXTraitY 特征对象实现的(这些在参考的“对象类型”部分中有介绍),允许在需要实现值的地方处理它(在指针后面)TraitX。工作用法涉及一些额外的Sized?注释,这些注释表示它们附加到的任何东西都是可选的 ( ?) 大小(默认是假定大小的东西)。

#![allow(dead_code)]

// indicate that it's OK to implement this trait for DSTs
trait TraitX for Sized? { } 
trait TraitY { }
trait TraitZ { }

impl<'a> TraitX for TraitY+'a { }

// the Sized? is to allow DSTs to be passed to this.
fn example<Sized? T: TraitX>(_: &T) {}

fn call_it(x: &TraitY, _y: &TraitZ) {
    example::<TraitY>(x); // only possible if `TraitY` impls `TraitX`.

    // error:
    // example::<TraitZ>(_y);  // `TraitZ` doesn't impl `TraitX`.
}

fn main() {}

围栏

::<TraitY>现在调用具有未定义类型的函数时需要显式类型提示,但这是一个错误#17178。目前,DST 仍然存在相当多的错误,因此在实践中实际使用并不容易,但这会有所改善。

DST 的主要动机是使处理 trait 对象与其他指针类型更加一致,例如,我们目前仅支持&TraitBox<Trait>trait 对象,但 DST 被设计为允许其他指针类型,如Rc<Trait>, Arc<Trait>。DST 还允许将它们视为真正的指针,例如,如果现在只能使用 DST,那么以前它是非法的,因为 trait 对象是胖指针,而不是普通指针obj: Box<Trait>&*obj

于 2014-10-05T11:53:36.133 回答
3

自从提出这个问题以来,Rust 发生了很大的变化。虽然目前*仍然支持该语法,但现在应该使用关键字指定 trait 对象dyn

trait TraitX { }
trait TraitY { }
impl TraitX for dyn TraitY { }

这完全等同于问题中的代码,但更明显的是它的含义:TraitXtrait object dyn TraitY实现。

例如:

struct Thing;
impl TraitY for Thing {}

fn main() {
    // Treat the &Thing as a dynamic object
    let object: &dyn TraitY = &Thing;

    // The TraitY object can still be used where a TraitX is expected
    do_something(object);
}

fn do_something<T: TraitX + ?Sized>(t: &T) {
}

从表面上看,正如您所提到的,它似乎类似于:

impl<A: TraitY> TraitX for A { }

这实现了实现TraitX的任何具体类型TraitY,但不包括trait 对象,因为Sized它始终是类型参数的隐式绑定。但是我们可以通过明确选择跳出边界来消除这个限制Sized

impl<A: TraitY + ?Sized> TraitX for A { }

这包括实现的所有具体类型TraitY,但现在包括动态TraitY对象。为了获得最大的灵活性,您应该使用此表格而不是上述任何一种替代方案。


* “当前”,因为在这些情况下,Rust 的未来版本可能需要关键字。至少,默认的 linter 不允许省略它

于 2019-02-09T13:18:56.127 回答