Цепочка последовательности вещей, которыми владеют или на которые ссылаются - PullRequest
0 голосов
/ 22 сентября 2018

Я пытаюсь найти черту для вещей, которые могут либо просто содержать другие вещи, либо создавать их по требованию, учитывая название вещи.Те, что содержатся, должны, в свою очередь, делать то же самое, создавая своего рода иерархию.Вот минимальный код:

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, чтобы он возвращался вместе с последним элементом, но это выглядит как довольно неуклюжий способ сделать это.Есть ли лучшее решение?

1 Ответ

0 голосов
/ 22 сентября 2018

Возвращаемое значение 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 s, но это поставит вас в неловкое положение.позиция при реализации Elem: вы не можете вернуть ссылку на self, поэтому практически все должно быть Boxed.

Трудно дать хорошую рекомендацию, учитывая неопределенность примера, ноЯ предлагаю вам сделать шаг назад и подумать, является ли BoxOrRef правильной абстракцией здесь.По сути, вы ничего не можете сделать с BoxOrRef, что вы не можете сделать со ссылкой, потому что BoxOrRef может быть ссылкой.В то же время, вы не можете ничего с этим сделать, что не можете сделать с Box, потому что это может быть Box.std::borrow::Cow использует ToOwned для реализации Clone и into_owned - возможно, подобный подход может работать для вас.(И если вы можете, возможно, просто внедрите ToOwned для dyn Elem и используйте Cow напрямую.)

...