当前位置:  开发笔记 > 后端 > 正文

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

如何解决《为什么我要在特征上实现方法而不是作为特征的一部分?》经验,为你挑选了2个好方法。

在尝试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编程语言部分没有提到这一点.搜索锈源本身,好像只有和使用这种特殊的功能.我没有看到在我看过源代码的少数板条箱中使用过的.AnyError



1> Francis Gagn..:

当您定义一个名为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 'anot 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 u8u16),因此我们可以得出这样的结论的类型&self会有所不同,从一个执行者到其他的(这将是&u8u8实施者和&u16u16实施者-不特质对象).但是,在固有方法中,我们得到类型id为dyn Foo(+ 'static),因此我们可以得出结论:类型&self总是&dyn Foo(特征对象).



2> Matthieu M...:

怀疑原因很简单:可能被覆盖或不被覆盖?

trait块中实现的方法可以被实现者覆盖trait,它只提供默认值.

另一方面,impl不能覆盖在块中实现的方法.

如果这个推理是正确的,那么你得到的错误y.foo_in_impl()只是缺乏润色:它应该有效. 请参阅FrancisGagné关于与生命期相互作用的更完整答案.

推荐阅读
手机用户2402852307
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有