链接拥有或引用的事物序列

  • 本文关键字:拥有 引用 链接 rust lifetime
  • 更新时间 :
  • 英文 :


我试图为事物设置一个特征,该特征可以简单地包含其他事物,也可以根据需要创建它们,给定事物的名称。那些包含的东西反过来应该能够做同样的事情,创建各种各样的层次结构。下面是一个最小的代码:

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的返回类型以将其与最终元素一起返回,但这让我觉得这样做的方式相当笨拙。有没有更好的解决方案?

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,从而使引用无效。

据推测,这实际上并没有发生,因此您必须告诉编译器。一种方法是将&selfBoxOrRef<'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做的事情,因为它可能是Boxstd::borrow::Cow使用ToOwned来实现Cloneinto_owned- 也许类似的方法可以为你工作。(如果可以的话,也许只需为dyn Elem实现ToOwned并直接使用Cow

最新更新