Проблемы со временем жизни с не копируемыми собственными значениями при попытке использовать их после передачи в функцию

Я сталкиваюсь с некоторыми проблемами с временем жизни переменных в Rust. x переменная в do_stuff заимствовано для try_wrap и, таким образом, не могут быть возвращены в None дело. Я думаю об этом неправильно?

struct NonCopyable;

impl NonCopyable {
    fn new() -> Self {
        NonCopyable
    }
}

fn do_stuff() -> NonCopyable {
    let x = NonCopyable::new();
    match try_wrap(x) {
        Some(val) => val,
        None => x,
    }
}

fn try_wrap(x: NonCopyable) -> Option<NonCopyable> {
    None
}

fn main() {}
error[E0382]: use of moved value: `x`
  --> src/main.rs:13:17
   |
11 |     match try_wrap(x) {
   |                    - value moved here
12 |         Some(val) => val,
13 |         None => x,
   |                 ^ value used here after move
   |
   = note: move occurs because `x` has type `NonCopyable`, which does not implement the `Copy` trait

2 ответа

Решение

с временем жизни переменных

Здесь нет жизни

x переменная в do_stuff заимствовано

Нет.

Я думаю об этом неправильно?

Да. Заимствование обозначено амперсандом & и / или параметр времени жизни 'foo:

&i32    // a borrowed integer
&'a str // a borrowed string slice with a lifetime
Foo<'b> // a type that contains a borrow of some kind

Ваш try_wrap функция берет на себя ответственность x:

fn try_wrap(x: NonCopyable) -> Option<NonCopyable>

Это означает x ушел, и вызывающая функция не может получить к нему доступ больше. Он был перенесен в try_wrap, который теперь может свободно делать все, что захочет со значением, в том числе уничтожать его. Вот почему вызывающая функция больше не может безопасно получить к ней доступ, и поэтому вы получаете сообщение об ошибке.

Если тип реализован Copy вместо этого компилятор неявно создал бы копию значения и передал ее. Если реализован тип Clone Вы могли бы явно позвонить .clone() на доводе try_wrap чтобы сохранить местное значение.

Как отмечает Флориан Ваймер, вы можете использовать тип для возврата либо обернутого значения, либо оригинала. Трудно сказать, основываясь на вашем примере, но я не согласен с использованием Result если это не ошибка. Вместо этого я бы создал свой собственный одноразовый enum или использовал бы что-то вроде Either:

extern crate either;

use either::Either;

fn do_stuff() -> NonCopyable {
    let x = NonCopyable::new();
    match try_wrap(x) {
        Either::Left(val) => val,
        Either::Right(x) => x,
    }
}

fn try_wrap(x: NonCopyable) -> Either<NonCopyable, NonCopyable> {
    Either::Right(x)
}

Вы также можете встроить логику try_wrap Вернуться в do_stuff или разделить try_wrap так что логика не требует владения:

fn do_stuff() -> NonCopyable {
    let x = NonCopyable::new();
    if should_wrap(&x) { do_wrap(x) } else { x }
}

fn should_wrap(x: &NonCopyable) -> bool { false }
fn do_wrap(x: NonCopyable) -> NonCopyable { x }

Поскольку вы возвращаете один и тот же тип, также возможно, что вы захотите получить изменяемую ссылку на значение и просто выполнить все необходимые условные изменения:

fn do_stuff() -> NonCopyable {
    let mut x = NonCopyable::new();
    try_wrap(&mut x);
    x
}

fn try_wrap(x: &mut NonCopyable) {}

Я думаю Option<NonCopyable> просто неправильный тип возврата для try_wrap, Вам нужен двуручный тип суммы здесь Result, так что вызывающая сторона может восстановить аргумент в случае ошибки, возможно, так:

fn do_stuff() -> NonCopyable {
    let x = NonCopyable::new();
    match try_wrap(x) {
        Ok(val) => val,
        Err(x) => x,
    }
}

fn try_wrap(x: NonCopyable) -> Result<NonCopyable, NonCopyable> {
    Err(x)
}
Другие вопросы по тегам