Проблемы со временем жизни с не копируемыми собственными значениями при попытке использовать их после передачи в функцию
Я сталкиваюсь с некоторыми проблемами с временем жизни переменных в 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)
}