我正在尝试为某些事物赋予特征,这些事物可以简单地包含其他事物,也可以根据事物的名称按需创建它们。反过来,那些包含的事物也应该能够做到这一点,从而创建各种层次结构。这是最少的代码:
use std::ops::Deref;
pub enum BoxOrRef<'a, T: ?Sized + 'a> {
Boxed(Box<T>),
Ref(&'a T),
}
impl<'a, T: ?Sized + 'a> Deref for BoxOrRef<'a, T> {
type Target = T;
fn deref(&self) -> &T {
match self {
BoxOrRef::Boxed(b) => &b,
BoxOrRef::Ref(r) => r,
}
}
}
pub trait Elem {
fn get_subelem<'a, 'b>(&'a self, name: &'b str) -> Option<BoxOrRef<'a, dyn Elem>>;
}
pub trait Table {
fn get_elem<'a, 'b>(&'a self, name: &'b str) -> Option<BoxOrRef<'a, dyn Elem>>;
}
fn resolve_name<'a, T: Table + ?Sized>(
table: &'a T,
name: &[String],
) -> Option<BoxOrRef<'a, dyn Elem>> {
let mut segments = name.iter();
if let Some(first_segment) = segments.next() {
segments.fold(table.get_elem(&first_segment), |res, next| {
res.and_then(|elem| elem.get_subelem(next))
})
} else {
None
}
}
但是,生命周期检查器对此不满意:
error[E0597]: `elem` does not live long enough
--> src/lib.rs:33:33
|
33 | res.and_then(|elem| elem.get_subelem(next))
| ^^^^ - borrowed value only lives until here
| |
| borrowed value does not live long enough
|
note: borrowed value must be valid for the lifetime 'a as defined on the function body at 26:17...
--> src/lib.rs:26:17
|
26 | fn resolve_name<'a, T: Table + ?Sized>(
| ^^
我需要以某种方式延长中间res
的寿命。我想我可以将它们放在一个结构中并调整resolve_name
的返回类型,以将其与最终元素一起返回,但这使我感到相当笨拙。有更好的解决方案吗?
答案 0 :(得分:3)
get_subelem
的返回值不能超过您用来调用它的&self
借项,因为get_subelem
的签名如此明确地表示:
fn get_subelem<'a, 'b>(&'a self, name: &'b str) -> Option<BoxOrRef<'a, dyn Elem>>;
// ^^ ^^
为了获得BoxOrRef<'a, _>
,您必须在生命周期self
内借用'a
。在调用方中,elem
不能超过其所属的闭包,并且get_subelem
借用了elem
,因此它也无法返回可以逃避该闭包的值。
您正在尝试做一些不安全的事情,并且编译器可以阻止您。从理论上讲,table.get_elem
可以返回一个Boxed
值,而elem.get_subelem
可以返回一个内部引用,然后当闭包返回时Box
将被丢弃,从而使引用无效。
大概这实际上没有发生,所以您必须告诉编译器。一种方法是将&self
与BoxOrRef<'a, _>
分离:
pub trait Elem<'a> {
fn get_subelem(&self, name: &str) -> Option<BoxOrRef<'a, dyn Elem<'a>>>;
}
以上更改将使您的示例在将生命周期参数添加到所有Elem
时进行编译,但是在实现Elem
时会使您陷入尴尬的境地:您无法返回对self
,因此实际上所有内容都必须为Boxed
。
鉴于示例的含糊之处,很难做出很好的建议,但是我建议您退后一步,考虑一下BoxOrRef
在这里是否是正确的抽象。从根本上讲,您无法对BoxOrRef
进行任何操作,而您无法对引用进行操作,因为BoxOrRef
可能成为。同时,您无法使用Box
做任何事情,因为它可能是Box
。 std::borrow::Cow
使用ToOwned
来实现Clone
和into_owned
-也许类似的方法可能对您有用。 (如果可以的话,也许只需为ToOwned
实现dyn Elem
并直接使用Cow
。)