【问题标题】:Lifetime error when making parameter generic使参数泛型时的生命周期错误
【发布时间】:2020-03-31 14:49:30
【问题描述】:

我已将问题简化为以下代码:

struct Struct<'a, 'b, T> {
    a: &'a T,
    b: &'b T,
}

trait Trait<'a, 'b, T> {
    fn a(&self) -> &'a T;
    fn b(&self) -> &'b T;
}

impl<'a, 'b, T> Trait<'a, 'b, T> for Struct<'a, 'b, T> {
    fn a(&self) -> &'a T {
        self.a
    }
    fn b(&self) -> &'b T {
        self.b
    }
}

struct Confused<T> {
    field: T,
}

impl<T> Confused<T> {
    fn foo<'a, 'b>(&'a self, param: &Struct<'a, 'b, T>) -> &'a T {
        param.b();
        param.a()
    }

    fn bar<'a, 'b, U: Trait<'a, 'b, T>>(&'a self, param: &U) -> &'a T {
        param.b();
        param.a()
    }
}

函数foo 没问题,但是当我将具体类型Struct&lt;'a, 'b, T&gt; 替换为泛型类型U: Trait&lt;'a, 'b, T&gt; 时,出现以下错误:

error[E0309]: the parameter type `T` may not live long enough
  --> src/lib.rs:31:15
   |
24 | impl<T> Confused<T> {
   |      - help: consider adding an explicit lifetime bound `T: 'b`...
...
31 |         param.b();
   |               ^
   |
note: ...so that the reference type `&'b T` does not outlive the data it points at
  --> src/lib.rs:31:15
   |
31 |         param.b();
   |               ^

添加绑定T: 'b 的建议对我来说没有意义,因为'bbar() 的参数。如何修复 bar() 以接受 Trait&lt;'a, 'b, T&gt; 的任何实现作为参数?

【问题讨论】:

  • 您的问题是什么?到目前为止,您只陈述了事实。
  • 我已经明确添加了我的问题
  • 您说编译器建议的界限对您来说没有意义,但是是什么阻止您添加它,即使您不理解它?
  • 'b 作为生命周期参数添加到Confused 会更改代码的含义。 foo()bar() 在两个生命周期中都是通用的。为什么foo() 可以,bar() 不行?
  • 为什么要将约束添加到Confused 而不是bar

标签: rust


【解决方案1】:

当你写一个泛型类型比如:

struct Foo<'a, T> {
    a: &'a T,
}

Rust 自动添加了 T: 'a 类型的隐式限制,因为您对 T 的引用不能比 T 本身更长寿。这是自动的,因为没有它您的类型将无法工作。

但是当你做这样的事情时:

impl<T> Foo {
    fn bar<'a, 'b>() -> &'a T {/*...*/}
}

有一个自动的T: 'a,但没有T: 'b,因为在任何地方都没有&amp;'b T

解决方案是自己添加这些约束。在您的代码中,它会是这样的:

impl<T> Confused<T> {
    fn bar<'a, 'b, U: Trait<'a, 'b, T>>(&'a self, param: &U) -> &'a T
    where
        T: 'b, //<--- here!
    {
        param.b();
        param.a()
    }
}

【讨论】:

  • 所以对于foo()T: 'b 约束由Struct&lt;'a, 'b, T&gt; 隐含。但是U: Trait&lt;'a, 'b, T&gt; 不会发生同样的情况,即使如果该界限不成立,就不可能实现该特征。
  • @TavianBarnes:是的,我认为正是如此。有趣的是你不能实现Trait&lt;a、b, T&gt;,除非T: 'a + 'b,但是你可以使用Trait&lt;a、b, T&gt;,即使T: !'b只要你不要打电话给Trait::b。在您的原始代码中,如果您删除对param.b() 的调用,它将编译得很好。
  • 看起来这可能由github.com/rust-lang/rfcs/pull/2089 修复,但尚未实现。
  • @TavianBarnes:我认为此更改不适用于您的代码。请注意,添加到原始代码中的任何自动要求都将是一项重大更改,因为目前您的代码可以在没有 param.b() 的情况下编译,但添加 T: 'b 自动会破坏它。
  • 添加的隐含边界究竟会破坏什么?只有{ param.a() } 会继续编译,而我现有的代码将不会中断。不可能有任何bar() 的调用者会被新绑定打破,因为Trait 的所有实例都已经满足它。
猜你喜欢
  • 1970-01-01
  • 2017-05-08
  • 2021-05-24
  • 1970-01-01
  • 2016-12-02
  • 1970-01-01
  • 2020-01-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多