Как обойти «невозможно вернуть значение, ссылающееся на временное значение», созданное при замыкании?

У меня есть struct, Thing, который реализует Clone, но не Copy, и у него есть два метода: один для выполнения работы, а другой для получения «потомка» Thing от существующего «родителя» Thing. Не у каждого Thing есть ребенок, поэтому иногда детское происхождение возвращается None.

Вот API:

#[derive(Clone)]
struct Thing;

impl Thing {
    fn do_work(&self) {}
    
    // This is considered expensive
    fn try_get_child(&self) -> Option<Thing> {
        Some(Thing)
    }
}

Я хотел бы реализовать функцию, описанную ниже:

/// If `b` is Some then do work on the Thing inside
/// Otherwise, try to get a child from `a` and do work on that child
/// If both `b` and `a.try_get_child()` are None, do nothing.
fn f(a: Thing, b: Option<&Thing>) {
    b.or_else(|| a.try_get_child().as_ref()).map(Thing::do_work);
}

Но эта реализация вызывает ошибку компиляции:

cannot return value referencing temporary value

Кажется, я понимаю, что эта ошибка возникает, потому что ни замыкания, ни функции не могут возвращать ссылки на значения, созданные внутри их тел, поскольку такие значения отбрасываются при возврате выполнения из функции или замыкания. Но каков идиоматический способ реализации fn f, чтобы дочерний вывод выполнялся только в случае необходимости? Возможно ли использовать методы Option?

так как это английское слово, в следующий раз используйте Foo и Bar

Stargateur 27.08.2024 04:30
Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
0
1
50
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Используя вспомогательные методы Option, если вы хотите дублировать код (конечно, вы можете извлечь его в функции):

fn f(a: Thing, b: Option<&Thing>) {
    b.map(Thing::do_work).or_else(|| a.try_get_child().as_ref().map(Thing::do_work));
}

Без дублирования кода (или если возвращаемое значение do_work() заимствовано из Thing), используя хитрый трюк для использования возможно-инициализированной переменной:

fn f(a: Thing, b: Option<&Thing>) {
    let a_child;
    let value = match b {
        Some(_) => b,
        None => {
            a_child = a.try_get_child();
            a_child.as_ref()
        }
    };
    value.map(Thing::do_work);
}

Вот что я получил

fn f(a: Thing, b: Option<&Thing>) {
    // Option 1
    // verbose `if let` solution
    if let Some(t) = b {
        t.do_work()
    } else if let Some(t) = a.try_get_child().as_ref() {
        t.do_work()
    }


    // Option 2
    // Need to compute `a.try_get_child()` even b is `Some(_)`
    // If `try_get_child` is not expensive, this look quite good i guess
    match (b, a.try_get_child().as_ref()) {
        (Some(t), _) | (None, Some(t)) => t.do_work(),
        _ => {}
    }


    // Option 3
    // One-liner but still need to compute `a.try_get_child()` whether or not b is `None`
    b.map_or(a.try_get_child().as_ref(), |_| b)
        .map(Thing::do_work);
}

Другие вопросы по теме