在尝试Any
更好地理解这个特性的同时,我看到它有一个impl
阻碍特质本身.我不明白这个结构的目的,或者即使它有一个特定的名称.
我用"普通"特征方法和impl
块中定义的方法做了一个小实验:
trait Foo { fn foo_in_trait(&self) { println!("in foo") } } impl dyn Foo { fn foo_in_impl(&self) { println!("in impl") } } impl Foo for u8 {} fn main() { let x = Box::new(42u8) as Box; x.foo_in_trait(); x.foo_in_impl(); let y = &42u8 as &dyn Foo; y.foo_in_trait(); y.foo_in_impl(); // May cause an error, see below }
编者注
在Rust的版本中,包括Rust 1.15.0,该行
y.foo_in_impl()
会导致错误:error: borrowed value does not live long enough --> src/main.rs:20:14 | 20 | let y = &42u8 as &Foo; | ^^^^ does not live long enough ... 23 | } | - temporary value only lives until here | = note: borrowed value must be valid for the static lifetime...
后续版本中不再出现此错误,但答案中说明的概念仍然有效.
从这个有限的实验中,似乎impl
块中定义的方法比块中定义的方法更具限制性trait
.可能会有一些额外的东西,以这种方式解锁,但我只是不知道它是什么!^ _ ^
关于traits和trait对象的Rust编程语言部分没有提到这一点.搜索锈源本身,好像只有和使用这种特殊的功能.我没有看到在我看过源代码的少数板条箱中使用过的.Any
Error
当您定义一个名为Foo
可以作为对象的特征时,Rust还定义了一个名为的特征对象类型dyn Foo
.在旧版本的Rust中,只调用了这种类型Foo
(请参阅"dyn"在类型中的含义是什么?).为了向后兼容这些旧版本,Foo
仍然可以命名特征对象类型,尽管dyn
语法应该用于新代码.
Trait对象具有一个生命周期参数,用于指定实现者的生命周期参数中最短的参数.要指定该生命周期,请将类型写为dyn Foo + 'a
.
当您编写impl dyn Foo {
(或仅impl Foo {
使用旧语法)时,您没有指定该生命周期参数,它默认为'static
.来自编译器的y.foo_in_impl();
声明提示:
注意:借来的值必须对静态寿命有效...
我们所要做的就是让这个更宽松的是impl
在任何生命周期内写一个泛型:
impl<'a> dyn Foo + 'a { fn foo_in_impl(&self) { println!("in impl") } }
现在,注意self
参数on foo_in_impl
是一个借来的指针,它有一个自己的生命周期参数.self
完整形式的类型看起来&'b (dyn Foo + 'a)
(由于运算符优先级,括号是必需的).A Box
拥有它u8
- 它不借用任何东西 - 所以你可以创造一个&(dyn Foo + 'static)
它.另一方面,&42u8
创建一个&'b (dyn Foo + 'a)
where 'a
not not 'static
,因为它42u8
被放入堆栈中的隐藏变量,而trait对象借用了这个变量.(但这确实没有意义; u8
不借用任何东西,所以它的Foo
实现应始终兼容dyn Foo + 'static
...... 42u8
从堆栈借来的事实应该影响'b
,而不是'a
.)
另外需要注意的是,特征方法是多态的,即使它们具有默认实现并且它们没有被覆盖,而特征对象上的固有方法是单形的(只有一个函数,无论特征背后是什么).例如:
use std::any::TypeId; trait Foo { fn foo_in_trait(&self) where Self: 'static, { println!("{:?}", TypeId::of::()); } } impl dyn Foo { fn foo_in_impl(&self) { println!("{:?}", TypeId::of:: ()); } } impl Foo for u8 {} impl Foo for u16 {} fn main() { let x = Box::new(42u8) as Box ; x.foo_in_trait(); x.foo_in_impl(); let x = Box::new(42u16) as Box ; x.foo_in_trait(); x.foo_in_impl(); }
样本输出:
TypeId { t: 10115067289853930363 }
TypeId { t: 1357119791063736673 }
TypeId { t: 14525050876321463235 }
TypeId { t: 1357119791063736673 }
在性状的方法,我们得到的基本类型(这里的类型ID u8
或u16
),因此我们可以得出这样的结论的类型&self
会有所不同,从一个执行者到其他的(这将是&u8
为u8
实施者和&u16
对u16
实施者-不特质对象).但是,在固有方法中,我们得到类型id为dyn Foo
(+ 'static
),因此我们可以得出结论:类型&self
总是&dyn Foo
(特征对象).
我怀疑原因很简单:可能被覆盖或不被覆盖?
在trait
块中实现的方法可以被实现者覆盖trait
,它只提供默认值.
另一方面,impl
不能覆盖在块中实现的方法.
如果这个推理是正确的,那么你得到的错误 请参阅FrancisGagné关于与生命期相互作用的更完整答案.y.foo_in_impl()
只是缺乏润色:它应该有效.