2016-12-16 3 views
12

Что здесь происходит (playground)?Не может заимствовать как неизменный, потому что он также заимствован как изменяемый в аргументах функции

struct Number { 
    num: i32 
} 

impl Number { 
    fn set(&mut self, new_num: i32) { 
     self.num = new_num; 
    } 
    fn get(&self) -> i32 { 
     self.num 
    } 
} 

fn main() { 
    let mut n = Number{ num: 0 }; 
    n.set(n.get() + 1); 
} 

дает эту ошибку:

error[E0502]: cannot borrow `n` as immutable because it is also borrowed as mutable 
    --> <anon>:17:11 
    | 
17 |  n.set(n.get() + 1); 
    |  - ^  - mutable borrow ends here 
    |  |  | 
    |  |  immutable borrow occurs here 
    |  mutable borrow occurs here 

Однако, если вы просто изменить код, чтобы это работает:

fn main() { 
    let mut n = Number{ num: 0 }; 
    let tmp = n.get() + 1; 
    n.set(tmp); 
} 

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

+0

Подробные сведения о процессе проверки заимствования можно найти здесь (https://github.com/rust-lang/rust/tree/master/src/librustc_borrowck/borrowck). – ljedrz

+1

Существует макрос [unborrow macro] (https://github.com/durka/unborrow) для обходного пути и обсуждение [reddit] (https://www.reddit.com/r/rust/comments/575tc7/why_does_rust_not_allow_disjoint_mutable_borrows /) – wimh

+0

Ага, спасибо wimh! – Timmmm

ответ

11

Эта линия:

n.set(n.get() + 1); 

является обессахаренная в

Number::set(&mut n, n.get() + 1); 

Сообщение об ошибке может быть немного более теперь ясно:

error[E0502]: cannot borrow `n` as immutable because it is also borrowed as mutable 
    --> <anon>:18:25 
    | 
18 |  Number::set(&mut n, n.get() + 1); 
    |      -^  - mutable borrow ends here 
    |      | | 
    |      | immutable borrow occurs here 
    |      mutable borrow occurs here 

Как Руст оценивает аргументы слева направо, этот код эквивалентен этому:

let arg1 = &mut n; 
let arg2 = n.get() + 1; 
Number::set(arg1, arg2); 

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

Это было сначала создано как bug #6268, теперь оно интегрировано в RFC 811.

Начиная с 1.25.0-nightly 2018-01-13, это possible to use non-lexical lifetimes. Если вы запустите свой пример на ночной ржавчине и включите ворота функции NLL, используя #![feature(nll)], это will now compile without an error.