在使用具有特征对象的Ref :: map时,无法推断生命周期

时间:2017-03-01 20:21:09

标签: rust lifetime

我有一个带有盒装特征(A)的结构Foo,另一个结构BoxedA,其中有一个Rc<RefCell<A>>。我尝试在BoxedA上创建一个方法,将方法返回到盒装特征,但在将Ref<A>映射到Ref<Foo>时仍然遇到生命周期问题。

这是我的代码:

use std::rc::Rc;
use std::cell::{RefCell, Ref};

trait Foo {

}

struct A {
    a: Box<Foo>
}

impl A {
    fn new(a: Box<Foo>) -> A {
        A { a: a }
    }

    fn a(&self) -> &Foo {
        &*self.a
    }
}

struct BoxedA {
    a: Rc<RefCell<A>>
}

impl BoxedA {
    fn new(a: Box<Foo>) -> BoxedA {
        BoxedA {
            a: Rc::new(RefCell::new(A::new(a)))
        }
    }

    fn a(&self) -> Ref<Foo> {
        Ref::map(self.a.borrow(), |a| a.a())
    }
}

impl Foo for i32 {

}

fn main() {
    let a = BoxedA::new(Box::new(3));

    let a_ref = a.a();
}

Rust操场链接:https://play.rust-lang.org/?gist=d0348ad9b06a152770f3877864b01531&version=stable&backtrace=0

我遇到以下编译错误:

error[E0495]: cannot infer an appropriate lifetime for autoref due to      conflicting requirements
  --> <anon>:34:41
   |
34 |         Ref::map(self.a.borrow(), |a| a.a())
   |                                         ^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the body at 34:38...
  --> <anon>:34:39
   |
34 |         Ref::map(self.a.borrow(), |a| a.a())
   |                                       ^^^^^
note: ...so that reference does not outlive borrowed content
  --> <anon>:34:39
   |
34 |         Ref::map(self.a.borrow(), |a| a.a())
   |                                       ^
note: but, the lifetime must be valid for the anonymous lifetime #1 defined on the body at 33:28...
  --> <anon>:33:29
   |
33 |       fn a(&self) -> Ref<Foo> {
   |  _____________________________^ starting here...
34 | |         Ref::map(self.a.borrow(), |a| a.a())
35 | |     }
   | |_____^ ...ending here
note: ...so that the declared lifetime parameter bounds are satisfied
  --> <anon>:34:9
   |
34 |         Ref::map(self.a.borrow(), |a| a.a())
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

奇怪的是,如果我用Foo替换所有i32,代码就会编译。

1 个答案:

答案 0 :(得分:2)

问题出在这个签名上:

fn a(&self) -> &Foo { ... }

根据终身省略规则,这扩展为:

fn a<'b>(&'b self) -> &'b (Foo + 'b) { ... }

哇,+ 'b位是什么?

Trait对象具有生命周期绑定,指定对象中包含的引用的最短生命周期。如果类型不包含任何引用,则该生存期限将为'static

如果在包含引用的类型上实现特征,例如一对引用:

impl<'a, 'b> Foo for (&'a u32, &'b u32) {}

然后你有一个&Foo类型的变量,它恰好是对(&'a u32, &'b u32)的引用(要清楚,那是对一对引用的引用),生命周期在哪里有关这两个&u32引用的信息是什么?特权对象的生命周期绑定的位置。&Foo的完整展开类型看起来像&'c (Foo + 'd)'d'a中最短的'b'static(或者可能更短,多亏了协方差)。

在许多其他地方,您没有明确指定生命周期约束。 期望函数的返回类型将默认为Box<Foo>生命周期绑定。例如,struct A中的Box<Foo + 'static>会发生什么:它实际上被解释为A::a

解决问题的简单方法是指定'static返回一个生命周期为fn a(&self) -> &(Foo + 'static) { ... } 的特征对象:

Box<Foo + 'static>

这很有道理,因为我们正在返回指向BoxedA::a内部的指针!

fn a(&self) -> Ref<Foo + 'static> { ... } 可能最终会导致您遇到类似的问题,因此您可能也希望在修复此问题时解决此问题:

A

既然你已经意识到这些生命周期边界,你可能想要考虑在生命周期限制内使BoxedA'static通用是否有意义,而不是强制执行{{1 }}。如果您想最大化通用性,您的代码将如下所示:

struct A<'a> {
    a: Box<Foo + 'a>
}

impl<'a> A<'a> {
    fn new(a: Box<Foo + 'a>) -> A<'a> {
        A { a: a }
    }

    fn a(&self) -> &(Foo + 'a) {
        &*self.a
    }
}

struct BoxedA<'a> {
    a: Rc<RefCell<A<'a>>>
}

impl<'a> BoxedA<'a> {
    fn new(a: Box<Foo + 'a>) -> BoxedA<'a> {
        BoxedA {
            a: Rc::new(RefCell::new(A::new(a)))
        }
    }

    fn a(&self) -> Ref<Foo + 'a> {
        Ref::map(self.a.borrow(), |a| a.a())
    }
}

由您来决定是否需要这种级别的通用性。