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

时间:2014-10-05 09:05:48

标签: syntax rust traits trait-objects

例如:

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中的任何内容?如果是这样,它的用例是什么?

2 个答案:

答案 0 :(得分: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>是大小三分球)。

语法正在为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>类型提示,但这是一个错误#17178。目前,仍有quite a few bugs with DST因此在实践中使用起来并不容易,但这会有所改善。

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

答案 1 :(得分:3)

自从提出这个问题以来,铁锈已经发生了很大变化。虽然目前仍支持该语法*,但现在应该使用关键字# assume built-in pthreads on MacOS IF(APPLE) set(CMAKE_THREAD_LIBS_INIT "-lpthread") set(CMAKE_HAVE_THREADS_LIBRARY 1) set(CMAKE_USE_WIN32_THREADS_INIT 0) set(CMAKE_USE_PTHREADS_INIT 1) set(THREADS_PREFER_PTHREAD_FLAG ON) ENDIF() 指定trait对象:

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 { } 的任何 concrete 类型实现TraitX,但 not 不会包含特征对象,因为TraitY始终类型参数的隐式绑定。但是我们可以通过显式退出Sized边界来消除该限制:

Sized

这包括实现impl<A: TraitY + ?Sized> TraitX for A { } 的所有具体类型,但现在 也包括动态TraitY对象。为了获得最大的灵活性,您应该使用这种形式,而不是上面的任何一种选择。


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