什么“TraitX for TraitY”在Rust中意味着什么?

问题描述 投票:3回答:2

例如:

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 { }
                          ^~~~~~

impl TraitX for TraitY(或具有明确生命周期的某些变体)是否意味着Rust中的任何内容?如果是这样,它的用途是什么?

syntax rust traits trait-objects
2个回答
3
投票

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

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

这完全等同于问题中的代码,但更明显的意思是:为特征对象TraitX实现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,但不包括特征对象,因为Sized始终是类型参数的隐式绑定。但我们可以通过明确选择退出Sized界限来消除这种限制:

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

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


*“当前”,因为Rust的未来版本可能在这些情况下需要关键字。至少,the default linter will disallow omitting it


4
投票

impl TraitX for TraitY正在使用TraitY作为a dynamically sized type (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 { }
         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

playpen

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

语法是为TraitX特征对象实现TraitY(这些在引用的"object types" section中有所涉及),允许在需要实现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() {}

playpen

当前调用具有unsized类型的函数时,需要显式的::<TraitY>类型提示,但这是一个bug #17178。目前,仍然有quite a few bugs with DST所以在实践中实际使用并不容易,但这会有所改善。

DST的主要动机是使处理特征对象与其他指针类型更加一致,例如:我们目前只支持&TraitBox<Trait>特征对象,但DST旨在允许其他指针类型,如Rc<Trait>Arc<Trait>。 DST还允许将那些像真正的指针一样处理,例如如果obj: Box<Trait>然后&*obj现在只能用DST,以前它是非法的,因为特质对象是胖指针,而不是正常的指针。

© www.soinside.com 2019 - 2024. All rights reserved.