list - 在 self 上指定生命周期时,Rust 在 trait 上使用动态多态性的问题

标签 list polymorphism rust self traits

我终于决定尝试一下 Rust(1.7 和 1.8)。来自 C++,我必须说 Rust 看起来很棒。我试图在 C++ 中重现一个众所周知的行为,该行为包括对一组对象使用动态多态性。

我遇到了一个我能够解决的非常烦人的问题,但我想知道你们对这个问题的看法,我希望它可以帮助其他正在尝试做同样事情的人。

让我们考虑以下实现初始想法的代码:

struct Foo
{
    foo: u32,
}
trait Bar
{
    fn bar(& self) -> u32;
}
impl Bar for Foo
{
    fn bar(& self) -> u32 
    {
        return self.foo; 
    }
}
fn foobar(l: &std::collections::LinkedList<& Bar>)
{
    for i in l
    {
        println!("{}", i.bar());
    }
}
fn main()
{
    let foo0 = Foo{foo: 8u32};
    let foo1 = Foo{foo: 8u32};
    let mut l = std::collections::LinkedList::new();
    l . push_back(&foo0 as &Bar);
    l . push_back(&foo1 as &Bar);
    foobar(&l);
}

这里一切都在编译,一切都在完美地工作。

我想传递另一个对 Bar 特征的函数 bar 的引用,结果我不得不向 Bar 添加生命周期> 特质。为了简单起见,我将只添加生命周期(因为它将在 Rust 1.8 下很好地编译)。通过整个代码添加生命周期后,代码最终如下所示:

struct Foo
{
    foo: u32,
}
trait Bar<'a>
{
    fn bar(& 'a self) -> u32;
}
impl<'a> Bar<'a> for Foo
{
    fn bar(& 'a self) -> u32 
    {
        return self.foo; 
    }
}
fn foobar<'a>(l: &std::collections::LinkedList<& 'a Bar>)
{
    for i in l
    {
        println!("{}", i.bar());
    }
}
fn main()
{
    let foo0 = Foo{foo: 8u32};
    let foo1 = Foo{foo: 8u32};
    let mut l = std::collections::LinkedList::new();
    l . push_back(&foo0 as &Bar);
    l . push_back(&foo1 as &Bar);
    foobar(&l);
}

如果编译这段代码,错误信息如下:

../test.rs:21:12: 21:13 error: cannot infer an appropriate lifetime due to conflicting requirements [E0495]
../test.rs:21   for i in l
                         ^
../test.rs:19:1: 25:2 help: consider using an explicit lifetime parameter as shown: fn foobar<'a>(l: &std::collections::LinkedList<&'a Bar>)
../test.rs:19 fn foobar<'a>(l: &std::collections::LinkedList<& 'a Bar>)
../test.rs:20 {
../test.rs:21   for i in l
../test.rs:22   {
../test.rs:23     println!("{}", i.bar());
../test.rs:24   }
              ...
error: aborting due to previous error

这里问题很明显,编译器知道 push_back 中给出的变量有不同的生命周期,因此它不能兼容和不同意我写的。如果在同一个let 语句中声明变量foo0foo1 就可以解决这个问题。

我发现很难找出该代码中的错误所在。 我的问题是:

  • 有没有办法告诉编译器一个集合可能需要不同的生命周期?

  • 通过在另一个引用变量(这里没有出现)而不是 self 上设置 'a 生命周期,代码可以编译。这是否意味着如果我们不指定任何生命周期,那么编译器将放置与我在上一个问题中询问的特定生命周期相对应的 '_

  • 是否存在“禁止”我们为自己设置生命周期的隐式规则?

  • 这段代码是 Rust 惯用的吗?

最佳答案

如果您将 foobar 的定义更改为:

,第二个代码示例将编译通过:
fn foobar<'a>(l: &std::collections::LinkedList<&'a Bar<'a>>) {
    for i in l {
        println!("{}", i.bar());
    }
}

但是,您对生命周期参数所做的事情对我来说意义不大。

当我们想要一个返回引用的方法时,我们通常会定义一个在整个生命周期内通用的特征(例如 Bar 的第二个版本)实现者。例如,假设我们有以下结构:

struct Foo<'a> {
    foo: &'a str,
}

此结构包含一个引用,我们必须明确命名该生命周期。我们希望允许任何生命周期,所以我们定义了一个生命周期参数,'a

我们可以向这个结构添加一个固有的方法:

impl<'a> Foo<'a> {
    fn foo(&self) -> &'a str {
        self.foo
    }
}

请注意 &self 没有明确的生命周期。相反,我们在 foo 方法的返回类型上使用 impl 中的 'a 参数,因为我们想要返回值的生命周期与结构中的生命周期相同,而不是 self 的生命周期(通常会更短)。

如果我们想类似地定义一个特征方法怎么办?

trait Bar<'a> {
    fn bar(&self) -> &'a str;
}

impl<'a> Bar<'a> for Foo<'a> {
    fn bar(&self) -> &'a str {
        self.foo
    }
}

trait Bar 定义了一个生命周期参数,impl 将它链接到 Foo 的生命周期参数。

除了在特征上添加生命周期参数,您还可以在各个方法上添加生命周期参数。

例如,考虑这个特征:

trait Slice {
    fn slice<'a>(&self, s: &'a str) -> &'a str;
}

我们希望结果与 s 参数具有相同的生命周期。为此,我们需要在方法上定义一个生命周期参数并将其应用于两个引用。

为了完整起见,这里是该特征的实现:

struct SliceBounds {
    start: usize,
    end: usize,
}

impl Slice for SliceBounds {
    fn slice<'a>(&self, s: &'a str) -> &'a str {
        &s[self.start..self.end]
    }
}

关于list - 在 self 上指定生命周期时,Rust 在 trait 上使用动态多态性的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35002610/

相关文章:

c++ - 如何修复由多态性引起的错误?

c# - 以 Child 类型作为参数的方法的继承类 : wrong method being called

rust - 为什么Arc <Mutex <T >>需要T : Sync + Send?

rust - 在传输不可复制的值时实现就地枚举修改

php - 如何从数据库中放入 <select> 类别和子类别?

c++ - 包含 C++ 中两个不同类的对象的列表

python - 如何在 Python 中将月份名称生成为列表?

c++ - 具有数字集 C++ 的多态性中的内存泄漏

rust - 通用特征和 slice::sort_by 的生命周期问题

c++ - 指向 Eigen3 vector 的 std::list 的指针或引用