我有一个带有盒装特征(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
,代码就会编译。
答案 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())
}
}
由您来决定是否需要这种级别的通用性。