2017-02-15 11 views
2

В this github discussion вы найдете код, который привлекает ярость заема проверки:Как избежать множественных изменчивых значений вектора при вставке значения, если вектор пуст?

fn main() { 
    let mut vec = vec!(); 

    match vec.first() { 
     None => vec.push(5), 
     Some(v) => unreachable!(), 
    } 
} 

Я понимаю, почему с мутацией в то время как неизменные заимствует выдающиеся проблематично. Я предположил, что решение было явно только один заем (изменяемые один), но она по-прежнему в результате у меня есть две заимствует, неизменный заем, а затем изменяемый заем:

fn main() { 
    let mut vec: Vec<i32> = vec!(); 

    let r_vec: &mut Vec<i32> = &mut vec; 

    match r_vec.first() { 
     None => r_vec.push(5), 
     Some(v) => unreachable!(), 
    } 
} 

компилятор все еще не устраивает:

error[E0502]: cannot borrow `*r_vec` as mutable because it is also borrowed as immutable 
--> testrust.rs:7:17 
    | 
6 |  match r_vec.first() { 
    |   ----- immutable borrow occurs here 
7 |   None => r_vec.push(5), 
    |     ^^^^^ mutable borrow occurs here 
8 |   Some(v) => unreachable!(), 
9 |  } 
    |  - immutable borrow ends here 

Почему мое обходное решение не работает, и каков правильный способ обойти эту проблему?

ответ

5

У вас нет. Ну, вы «избегаете» множественных заимствований ... не имея нескольких заимствований.

fn main() { 
    let mut vec = vec![]; 

    if vec.first().is_none() { 
     vec.push(5); 
    } 
} 

Еще более идиоматически:

if vec.is_empty() { 
    vec.push(5); 
} 

В обоих случаях мы заимствуем vec сделать вызов метода, но прекратить что позаимствовать перед телом if выполняется. Сравните это с match, где заимствование производится в выражении спички матча, а затем делится с оружием.


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

Это не то, как это работает. Вы должны понимать, как память играет и что такое ссылка. A Vec содержит указатель на память, в котором хранятся данные.

Когда вы получаете ссылку на данные, вектор, ссылка содержит адрес памяти для данных, а компилятор гарантирует, что есть только один из тех, кому разрешено мутировать Vec. Когда вы получите push значение, которому может потребоваться выделить новую память для хранения всех данных. Это может привести к аннулированию ссылки, которую вы держите. Если это произойдет, то в следующий раз, когда вы будете использовать ссылку, это будет указывать на другую, несвязанную часть памяти, ваша программа потерпит крах, ваши данные пользователей будут подвержены уязвимостям безопасности и т. Д. И т. Д. И т. Д. И т. Д.


вся точкой issue you linked и связанный с этим pre-RFC является то, что этот код должен быть в состоянии определить, как сейф:

match vec.first() { 
    None => vec.push(5), 
    Some(v) => unreachable!(), 
} 

в этом случае программист может видеть, что мы никогда не используем заимствовать в случае None, поэтому компилятор может теоретически чтобы закончить заимствование перед выполнением любого из совпадающих рычагов или иным образом сделать обе руки непересекающимися по времени жизни. Это не делает это сейчас.

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

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

Смотрите также: