为什么我要在特征上而不是作为特征的一部分来实现方法?
Why would I implement methods on a trait instead of as part of the trait?
在试图更好地理解 Any
特征时,我看到了它 has an impl
block for the trait itself。我不明白这个构造的目的,即使它有一个特定的名称。
我用 "normal" 特征方法和 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<dyn Foo>;
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
}
Editor's note
In versions of Rust up to and including Rust 1.15.0, the line
y.foo_in_impl()
causes the error:
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...
This error is no longer present in subsequent versions, but the
concepts explained in the answers are still valid.
从这个有限的实验来看,impl
块中定义的方法似乎比 trait
块中定义的方法更具限制性。这样做可能会解锁一些额外的东西,但我还不知道它是什么! ^_^
The Rust Programming Language traits and trait objects don't make any mention of this. Searching the Rust source itself, it seems like only Any
and Error
中的部分使用了此特定功能。在我查看源代码的少数板条箱中,我没有看到它被使用过。
我怀疑原因很简单:有没有被覆盖?
trait
块中实现的方法可以被 trait
的实现者覆盖,它只提供一个默认值。
另一方面,无法覆盖 impl
块中实现的方法。
如果这个推理是正确的,那么你为 y.foo_in_impl()
得到的错误只是缺乏润色:它应该有效。 见Francis Gagné 关于生命周期交互的更完整答案。
当你定义一个名为 Foo
的 trait 可以成为一个对象时,Rust 也定义了一个名为 dyn Foo
的 trait 对象类型。在旧版本的 Rust 中,这种类型只被称为 Foo
(参见 )。为了与这些旧版本向后兼容,Foo
仍然可以命名特征对象类型,尽管 dyn
语法应该用于新代码。
特征对象有一个生命周期参数,指定最短的实现者生命周期参数。要指定该生命周期,您可以将类型写为 dyn Foo + 'a
.
当您编写 impl dyn Foo {
(或仅使用旧语法 impl Foo {
)时,您并未指定生命周期参数,它默认为 'static
。编译器对 y.foo_in_impl();
语句的注释暗示了这一点:
note: borrowed value must be valid for the static lifetime...
我们要做的就是在任何生命周期内编写一个通用的impl
:
impl<'a> dyn Foo + 'a {
fn foo_in_impl(&self) { println!("in impl") }
}
现在,请注意 foo_in_impl
上的 self
参数是一个借用的指针,它有自己的生命周期参数。 self
的类型在其完整形式中看起来像 &'b (dyn Foo + 'a)
(由于运算符优先级,括号是必需的)。 Box<u8>
拥有它的 u8
——它没有借用任何东西——所以你可以从中创建一个 &(dyn Foo + 'static)
。另一方面,&42u8
创建一个 &'b (dyn Foo + 'a)
,其中 'a
不是 'static
,因为 42u8
被放入堆栈的隐藏变量中,并且特征object 借用了这个变量。 (不过,这并没有什么意义;u8
没有借用任何东西,所以它的 Foo
实现应该始终与 dyn Foo + 'static
兼容......事实上 42u8
从堆栈中借用应该影响 'b
,而不是 'a
。)
另一件需要注意的事情是特征方法是多态的,即使它们有一个默认实现并且它们没有被覆盖,而特征对象的固有方法是单态的(只有一个函数,不管背后是什么特征)。例如:
use std::any::type_name;
trait Foo {
fn foo_in_trait(&self)
where
Self: 'static,
{
println!("{}", type_name::<Self>());
}
}
impl dyn Foo {
fn foo_in_impl(&self) {
println!("{}", type_name::<Self>());
}
}
impl Foo for u8 {}
impl Foo for u16 {}
fn main() {
let x = Box::new(42u8) as Box<dyn Foo>;
x.foo_in_trait();
x.foo_in_impl();
let x = Box::new(42u16) as Box<Foo>;
x.foo_in_trait();
x.foo_in_impl();
}
示例输出:
u8
dyn playground::Foo
u16
dyn playground::Foo
在trait方法中,我们得到了底层类型的类型名(这里是u8
或u16
),所以我们可以得出结论&self
的类型会有所不同从一个实现者到另一个实现者(u8
实现者是 &u8
,u16
实现者是 &u16
——不是特征对象)。但是,在固有方法中,我们得到了dyn Foo
的类型名(+ 'static
),所以我们可以得出结论,&self
的类型总是&dyn Foo
(一个trait对象).
在试图更好地理解 Any
特征时,我看到了它 has an impl
block for the trait itself。我不明白这个构造的目的,即使它有一个特定的名称。
我用 "normal" 特征方法和 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<dyn Foo>;
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
}
Editor's note
In versions of Rust up to and including Rust 1.15.0, the line
y.foo_in_impl()
causes the error: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...
This error is no longer present in subsequent versions, but the concepts explained in the answers are still valid.
从这个有限的实验来看,impl
块中定义的方法似乎比 trait
块中定义的方法更具限制性。这样做可能会解锁一些额外的东西,但我还不知道它是什么! ^_^
The Rust Programming Language traits and trait objects don't make any mention of this. Searching the Rust source itself, it seems like only Any
and Error
中的部分使用了此特定功能。在我查看源代码的少数板条箱中,我没有看到它被使用过。
我怀疑原因很简单:有没有被覆盖?
trait
块中实现的方法可以被 trait
的实现者覆盖,它只提供一个默认值。
另一方面,无法覆盖 impl
块中实现的方法。
如果这个推理是正确的,那么你为 见Francis Gagné 关于生命周期交互的更完整答案。y.foo_in_impl()
得到的错误只是缺乏润色:它应该有效。
当你定义一个名为 Foo
的 trait 可以成为一个对象时,Rust 也定义了一个名为 dyn Foo
的 trait 对象类型。在旧版本的 Rust 中,这种类型只被称为 Foo
(参见 Foo
仍然可以命名特征对象类型,尽管 dyn
语法应该用于新代码。
特征对象有一个生命周期参数,指定最短的实现者生命周期参数。要指定该生命周期,您可以将类型写为 dyn Foo + 'a
.
当您编写 impl dyn Foo {
(或仅使用旧语法 impl Foo {
)时,您并未指定生命周期参数,它默认为 'static
。编译器对 y.foo_in_impl();
语句的注释暗示了这一点:
note: borrowed value must be valid for the static lifetime...
我们要做的就是在任何生命周期内编写一个通用的impl
:
impl<'a> dyn Foo + 'a {
fn foo_in_impl(&self) { println!("in impl") }
}
现在,请注意 foo_in_impl
上的 self
参数是一个借用的指针,它有自己的生命周期参数。 self
的类型在其完整形式中看起来像 &'b (dyn Foo + 'a)
(由于运算符优先级,括号是必需的)。 Box<u8>
拥有它的 u8
——它没有借用任何东西——所以你可以从中创建一个 &(dyn Foo + 'static)
。另一方面,&42u8
创建一个 &'b (dyn Foo + 'a)
,其中 'a
不是 'static
,因为 42u8
被放入堆栈的隐藏变量中,并且特征object 借用了这个变量。 (不过,这并没有什么意义;u8
没有借用任何东西,所以它的 Foo
实现应该始终与 dyn Foo + 'static
兼容......事实上 42u8
从堆栈中借用应该影响 'b
,而不是 'a
。)
另一件需要注意的事情是特征方法是多态的,即使它们有一个默认实现并且它们没有被覆盖,而特征对象的固有方法是单态的(只有一个函数,不管背后是什么特征)。例如:
use std::any::type_name;
trait Foo {
fn foo_in_trait(&self)
where
Self: 'static,
{
println!("{}", type_name::<Self>());
}
}
impl dyn Foo {
fn foo_in_impl(&self) {
println!("{}", type_name::<Self>());
}
}
impl Foo for u8 {}
impl Foo for u16 {}
fn main() {
let x = Box::new(42u8) as Box<dyn Foo>;
x.foo_in_trait();
x.foo_in_impl();
let x = Box::new(42u16) as Box<Foo>;
x.foo_in_trait();
x.foo_in_impl();
}
示例输出:
u8
dyn playground::Foo
u16
dyn playground::Foo
在trait方法中,我们得到了底层类型的类型名(这里是u8
或u16
),所以我们可以得出结论&self
的类型会有所不同从一个实现者到另一个实现者(u8
实现者是 &u8
,u16
实现者是 &u16
——不是特征对象)。但是,在固有方法中,我们得到了dyn Foo
的类型名(+ 'static
),所以我们可以得出结论,&self
的类型总是&dyn Foo
(一个trait对象).