Есть ли безопасный способ временно получить принадлежащее значение из изменяемой ссылки в Rust?

Я работаю с двумя отдельными функциями.

  • Первый берет принадлежащий экземпляр структуры, а затем возвращает его.
  • Второй принимает изменяемую ссылку, но должен использовать первую функцию.
// This structure is not `Clone`.
struct MyStruct;

fn take_owned(s: MyStruct) -> MyStruct {
    // Do things
    s
}

fn take_mut(s: &mut MyStruct) {
    *s = take_owned(s /* problem */);
}

Я думал о решении, но я не уверен, что оно правильное:

use std::ptr;

// Temporarily turns a mutable reference into an owned value.
fn mut_to_owned<F>(val: &mut MyStruct, f: F)
where
    F: FnOnce(MyStruct) -> MyStruct,
{
    // We're the only one able to access the data referenced by `val`.
    // This operation simply takes ownership of the value.
    let owned = unsafe { ptr::read(val) };

    // Do things to the owned value.
    let result = f(owned);

    // Give the ownership of the value back to its original owner.
    // From its point of view, nothing happened to the value because we have
    // an exclusive reference.
    unsafe { ptr::write(val, result) };
}

Используя эту функцию, я могу сделать это:

fn take_mut(s: &mut MyStruct) {
    mut_to_owned(s, take_owned);
}

Этот код звучит? Если нет, есть ли способ безопасно сделать это?


person Gymore    schedule 12.03.2021    source источник
comment
Основная проблема AFAIK заключается в том, что take_owned может паниковать, и в этом случае право собственности никогда не будет возвращено, что бы вы ни делали внутри вызывающего абонента.   -  person Cerberus    schedule 12.03.2021
comment
О да. Решит ли проблему использование чего-то вроде std::panic::catch_unwind?   -  person Gymore    schedule 12.03.2021


Ответы (1)


Кто-то уже реализовал то, что вы ищете, в ящике с именем take_mut.

Функция take_mut::take

pub fn take<T, F>(mut_ref: &mut T, closure: F) 
where
    F: FnOnce(T) -> T, 

Позволяет использовать значение, на которое указывает &mut T, как если бы оно принадлежало, если после этого становится доступным T.

...

Прервет программу, если закрытие паникует.

Функция take_mut::take_or_recover

pub fn take_or_recover<T, F, R>(mut_ref: &mut T, recover: R, closure: F) 
where
    F: FnOnce(T) -> T,
    R: FnOnce() -> T, 

Позволяет использовать значение, на которое указывает &mut T, как если бы оно принадлежало, если после этого становится доступным T.

...

Заменит &mut T на recover, если closure запаникует, а затем продолжит панику.

Реализация, по сути, то, что у вас есть, плюс обработка паники, как описано.

person Kevin Reid    schedule 12.03.2021