链接一系列拥有或引用的事物

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

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