Не удается удалить значение, которое находится за общей ссылкой при развертывании

#rust

Вопрос:

Это код, который я пытаюсь выполнить:

 fn my_fn(arg1: amp;Option<Box<i32>>) -> i32 {
    if arg1.is_none() {
        return 0;
    }
    let integer = arg1.unwrap();
    *integer
}

fn main() {
    let integer = 42;
    my_fn(amp;Some(Box::new(integer)));
}
 

(на ржавой игровой площадке)

Я получаю следующую ошибку в предыдущих версиях Rust:

 error[E0507]: cannot move out of borrowed content
 --> src/main.rs:5:19
  |
5 |     let integer = arg1.unwrap();
  |                   ^^^^ cannot move out of borrowed content
 

И в более современных версиях:

 error[E0507]: cannot move out of `*arg1` which is behind a shared reference
 --> src/main.rs:5:19
  |
5 |     let integer = arg1.unwrap();
  |                   ^^^^
  |                   |
  |                   move occurs because `*arg1` has type `std::option::Option<std::boxed::Box<i32>>`, which does not implement the `Copy` trait
  |                   help: consider borrowing the `Option`'s content: `arg1.as_ref()`
 

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

Почему это ошибка и как ее решить?

Ответ №1:

Option::unwrap() использует опцию, то есть принимает опцию по значению. Однако у вас нет значения, у вас есть только ссылка на него. Вот в чем заключается ошибка.

Ваш код должен быть идиоматически написан следующим образом:

 fn my_fn(arg1: amp;Option<Box<i32>>) -> i32 {
    match arg1 {
        Some(b) => **b,
        None => 0,
    }
}

fn main() {
    let integer = 42;
    my_fn(amp;Some(Box::new(integer)));
}
 

(на ржавой игровой площадке)

Или вы можете использовать Option комбинаторы , подобные Option::as_ref или Option::as_mut в паре с Option::map_or ними, как предложил Шепмастер:

 fn my_fn(arg1: amp;Option<Box<i32>>) -> i32 {
    arg1.as_ref().map_or(0, |n| **n)
}
 

В этом коде используется тот факт, что i32 он автоматически копируется. Если бы тип внутри Box не Copy был , то вы вообще не смогли бы получить внутреннее значение по значению — вы могли бы только клонировать его или возвращать ссылку, например, как здесь:

 fn my_fn2(arg1: amp;Option<Box<i32>>) -> amp;i32 {
    arg1.as_ref().map_or(amp;0, |n| n)
}
 

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

Комментарии:

1. И что, если я хочу получить ссылку на i32 внутри поля (допустим, я хочу изменить его и иметь другую часть программы, чтобы увидеть измененное значение) ?

2. ФУ, я бы написал это так arg1.as_ref().map(|x| **x).unwrap_or(0)

3. @Мебиус, я обновил свой ответ примером со ссылками, однако я не совсем понял, что вы имеете в виду, говоря об изменении значений. Пример был бы полезен, но в любом случае он, вероятно, заслуживает другого вопроса.

4. @VladimirMatveev никакого особого случая использования, я просто спросил из любопытства. Спасибо за решение !

Ответ №2:

Так как Rust 1.40 существует Option::as_deref , так что теперь вы можете это сделать:

 fn my_fn(arg1: amp;Option<Box<i32>>) -> i32 {
    *arg1.as_deref().unwrap_or(amp;0)
}