例如:
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中的任何内容?如果是这样,它的用途是什么?
自问这个问题以来,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。
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 { }
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
错误是说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() {}
当前调用具有unsized类型的函数时,需要显式的::<TraitY>
类型提示,但这是一个bug #17178。目前,仍然有quite a few bugs with DST所以在实践中实际使用并不容易,但这会有所改善。
DST的主要动机是使处理特征对象与其他指针类型更加一致,例如:我们目前只支持&Trait
和Box<Trait>
特征对象,但DST旨在允许其他指针类型,如Rc<Trait>
,Arc<Trait>
。 DST还允许将那些像真正的指针一样处理,例如如果obj: Box<Trait>
然后&*obj
现在只能用DST,以前它是非法的,因为特质对象是胖指针,而不是正常的指针。