Является ли ошибка «это представляет потенциальное неопределенное поведение в вашем коде» при использовании защиты от совпадения в баге в компиляторе? - PullRequest
2 голосов
/ 07 июня 2019

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

#[derive(Debug, Clone)]
enum Value {
    Int(i32),
    List(Vec<Value>),
}

#[derive(Debug)]
struct Error(&'static str, Value);

fn main() {
    let mut value = Value::List(vec![
        Value::Int(1),
        Value::Int(2),
        Value::Int(34),
        Value::Int(12),
    ]);
    let y = index_list(&mut value, 2);

    let _ = dbg!(y);
}

fn index_list<'a>(value: &'a mut Value, idx: usize) -> Result<&'a mut Value, Error> {
    match *value {
        Value::List(ref mut list) if idx < list.len() => Ok(&mut list[idx]),
        Value::List(_) => Err(Error("index out of range", value.clone())),
        _ => Err(Error("tried to index int", value.clone())),
    }
}

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

warning[E0502]: cannot borrow `*value` as immutable because it is also borrowed as mutable
  --> src/main.rs:25:59
   |
22 | fn index_list<'a>(value: &'a mut Value, idx: usize) -> Result<&'a mut Value, Error> {
   |               -- lifetime `'a` defined here
23 |     match *value {
24 |         Value::List(ref mut list) if idx < list.len() => Ok(&mut list[idx]),
   |                     ------------                         ------------------ returning this value requires that `value.0` is borrowed for `'a`
   |                     |
   |                     mutable borrow occurs here
25 |         Value::List(_) => Err(Error("index out of range", value.clone())),
   |                                                           ^^^^^ immutable borrow occurs here
   |
   = warning: this error has been downgraded to a warning for backwards compatibility with previous releases
   = warning: this represents potential undefined behavior in your code and this warning will become a hard error in the future

warning[E0502]: cannot borrow `*value` as immutable because it is also borrowed as mutable
  --> src/main.rs:26:46
   |
22 | fn index_list<'a>(value: &'a mut Value, idx: usize) -> Result<&'a mut Value, Error> {
   |               -- lifetime `'a` defined here
23 |     match *value {
24 |         Value::List(ref mut list) if idx < list.len() => Ok(&mut list[idx]),
   |                     ------------                         ------------------ returning this value requires that `value.0` is borrowed for `'a`
   |                     |
   |                     mutable borrow occurs here
25 |         Value::List(_) => Err(Error("index out of range", value.clone())),
26 |         _ => Err(Error("tried to index int", value.clone())),
   |                                              ^^^^^ immutable borrow occurs here
   |
   = warning: this error has been downgraded to a warning for backwards compatibility with previous releases
   = warning: this represents potential undefined behavior in your code and this warning will become a hard error in the future

Я не понимаю, почему строка Err(value.clone()) требует, чтобы заем Ok(&mut ...) все еще был активным, потому что они взаимоисключающие и оба приводят к возврату функции. Это предупреждение исчезнет, ​​если я уберу охрану на первой руке спички, но мне нужен этот охранник, чтобы быть там. Это ошибка в системе NLL? У меня никогда не было этой проблемы со старым заемщиком. Как я могу сделать это лучше?

1 Ответ

2 голосов
/ 08 июня 2019

Эта ошибка выглядит для меня как ограничение проверки заимствования, аналогично Ошибка двойного изменяемого заимствования в цикле происходит даже при NLL на . Я не понимаю, как это может привести к дыре в надежности, и считаю, что код безопасен.

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

fn bar<'a>(
    map: &'a mut HashMap<String, Vec<i32>>,
    name: &str,
    idx: usize,
) -> Result<&'a mut i32, Vec<i32>> {
    let value = map.get_mut(name).unwrap();
    if idx < value.len() {
        Ok(&mut value[idx])
    } else {
        Err(value.clone())
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...