链接一系列拥有或引用的事物
Chaining a sequence of things that are either owned or referenced
我正在尝试为可以简单地包含其他事物或根据需要创建它们的事物赋予特征,给定一个事物的名称。那些包含的东西反过来应该能够做同样的事情,创建一个层次结构。这是一个最小的代码:
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
的 return 类型与最后一个元素一起调整为 return ,但这让我觉得这是一种相当笨拙的做法。有更好的解决方案吗?
get_subelem
的 return 值不能比你以前叫它的 &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
,所以它也不能 return 一个可以逃脱闭包的值.
你试图做一些不安全的事情,编译器阻止你是对的。理论上,table.get_elem
可以return一个Boxed
值,elem.get_subelem
可以return一个内部引用,然后Box
会被丢弃闭包 returns,使引用无效。
大概这实际上并没有发生,所以你必须告诉编译器。一种方法是将 &self
与 BoxOrRef<'a, _>
分离:
pub trait Elem<'a> {
fn get_subelem(&self, name: &str) -> Option<BoxOrRef<'a, dyn Elem<'a>>>;
}
一旦您将生命周期参数添加到所有 Elem
,上述更改将使您的示例编译通过,但在实现 Elem
时,它会让您处于尴尬的境地:您不能 return 对 self
的引用,所以实际上所有内容都必须是 Boxed
.
鉴于示例的模糊性,很难提出好的建议,但我建议您退后一步,思考 BoxOrRef
是否是此处的正确抽象。从根本上说,你不能用 BoxOrRef
做任何你不能用引用做的事情,因为 BoxOrRef
可能 是 一个引用。同时,你不能用它做任何你不能用 Box
做的事情,因为它可能是 Box
。 std::borrow::Cow
使用 ToOwned
来实现 Clone
和 into_owned
—— 也许类似的方法对您有用。 (如果可以的话,也许只需为 dyn Elem
实现 ToOwned
并直接使用 Cow
。)
我正在尝试为可以简单地包含其他事物或根据需要创建它们的事物赋予特征,给定一个事物的名称。那些包含的东西反过来应该能够做同样的事情,创建一个层次结构。这是一个最小的代码:
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
的 return 类型与最后一个元素一起调整为 return ,但这让我觉得这是一种相当笨拙的做法。有更好的解决方案吗?
get_subelem
的 return 值不能比你以前叫它的 &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
,所以它也不能 return 一个可以逃脱闭包的值.
你试图做一些不安全的事情,编译器阻止你是对的。理论上,table.get_elem
可以return一个Boxed
值,elem.get_subelem
可以return一个内部引用,然后Box
会被丢弃闭包 returns,使引用无效。
大概这实际上并没有发生,所以你必须告诉编译器。一种方法是将 &self
与 BoxOrRef<'a, _>
分离:
pub trait Elem<'a> {
fn get_subelem(&self, name: &str) -> Option<BoxOrRef<'a, dyn Elem<'a>>>;
}
一旦您将生命周期参数添加到所有 Elem
,上述更改将使您的示例编译通过,但在实现 Elem
时,它会让您处于尴尬的境地:您不能 return 对 self
的引用,所以实际上所有内容都必须是 Boxed
.
鉴于示例的模糊性,很难提出好的建议,但我建议您退后一步,思考 BoxOrRef
是否是此处的正确抽象。从根本上说,你不能用 BoxOrRef
做任何你不能用引用做的事情,因为 BoxOrRef
可能 是 一个引用。同时,你不能用它做任何你不能用 Box
做的事情,因为它可能是 Box
。 std::borrow::Cow
使用 ToOwned
来实现 Clone
和 into_owned
—— 也许类似的方法对您有用。 (如果可以的话,也许只需为 dyn Elem
实现 ToOwned
并直接使用 Cow
。)