gpt4 book ai didi

rust - 为什么我要在特征上而不是作为特征的一部分来实现方法?

转载 作者:行者123 更新时间:2023-11-29 08:09:16 24 4
gpt4 key购买 nike

在试图更好地理解 Any 特性时,我看到它 has an impl block for the trait itself .我不明白这个构造的目的,或者即使它有一个特定的名称。

我用“普通”特性方法和 impl block 中定义的方法做了一个小实验:

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 block 中定义的方法似乎比 trait block 中定义的方法更具限制性。这样做可能会解锁一些额外的东西,但我还不知道它是什么! ^_^

来自 The Rust Programming Language 的部分 traitstrait objects不要提这个。搜索 Rust 源代码本身,似乎只有 AnyError使用此特定功能。在我查看源代码的少数 crate 中,我没有看到它被使用过。

最佳答案

当您定义名为 Foo 的特征时可以变成一个对象,Rust 还定义了一个名为 dyn Foo 的特征对象类型。 .在旧版本的 Rust 中,这种类型只被称为 Foo (参见 What does "dyn" mean in a type?)。为了向后兼容这些旧版本,Foo尽管 dyn 仍然可以命名特征对象类型语法应该用于新代码。

Trait 对象有一个生命周期参数,它指定了实现者生命周期参数中最短的一个。要指定该生命周期,您可以将类型写为 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") }
}

现在,请注意 self关于 foo_in_impl 的争论是一个借用的指针,它有自己的生命周期参数。 self 的类型,完整形式看起来像 &'b (dyn Foo + 'a) (由于运算符优先级,括号是必需的)。 Box<u8>拥有它的 u8 – 它不借用任何东西 –,所以你可以创建一个 &(dyn Foo + 'static)从它出来。另一方面,&42u8创建一个 &'b (dyn Foo + 'a)其中 'a不是 'static ,因为 42u8放在栈上的一个隐藏变量中,trait对象借用这个变量。 (虽然这并没有什么意义;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 方法中,我们得到了底层类型的类型名称(这里是 u8u16 ),所以我们可以得出 &self 的类型将因一个实现者而异(它将是 &u8 对于 u8 实现者和 &u16 对于 u16 实现者——不是特征对象)。然而,在固有方法中,我们得到了类型名称dyn Foo。 ( + 'static ), 所以我们可以得出结论 &self 的类型总是 &dyn Foo (特征对象)。

关于rust - 为什么我要在特征上而不是作为特征的一部分来实现方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53997145/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com