O que o erro significa neste caso:
fn main() {
let mut v: Vec<usize> = vec![1, 2, 3, 4, 5];
v[v[1]] = 999;
}
error[E0502]: cannot borrow `v` as immutable because it is also borrowed as mutable
--> src/main.rs:3:7
|
3 | v[v[1]] = 999;
| --^----
| | |
| | immutable borrow occurs here
| mutable borrow occurs here
| mutable borrow later used here
Eu descobri que a indexação é implementada via Index
eIndexMut
características e que v[1]
é açúcar sintático para *v.index(1)
. Equipado com esse conhecimento, tentei executar o seguinte código:
use std::ops::{Index, IndexMut};
fn main() {
let mut v: Vec<usize> = vec![1, 2, 3, 4, 5];
*v.index_mut(*v.index(1)) = 999;
}
Para minha surpresa, isso funciona perfeitamente! Por que o primeiro trecho não funciona, mas o segundo funciona? Do jeito que eu entendo a documentação, eles devem ser equivalentes, mas isso obviamente não é o caso.
rust
borrow-checker
Lucas Boucke
fonte
fonte
Respostas:
A versão sugerida é um pouco diferente do que você tem. A linha
realmente desugars para
Isso resulta na mesma mensagem de erro, mas as anotações dão uma dica do que está acontecendo:
A diferença importante para sua versão desejada é a ordem de avaliação. Os argumentos de uma chamada de função são avaliados da esquerda para a direita na ordem listada, antes de realmente fazer a chamada de função. Nesse caso, isso significa que primeiro
&mut v
é avaliado, mutuamente emprestadov
. Em seguida,Index::index(&v, 1)
deve ser avaliado, mas isso não é possível -v
já é mutuamente emprestado. Finalmente, o compilador mostra que a referência mutável ainda é necessária para a chamada de funçãoindex_mut()
, portanto, a referência mutável ainda está ativa quando a referência compartilhada é tentada.A versão que realmente compila tem uma ordem de avaliação ligeiramente diferente.
Primeiro, os argumentos da função para as chamadas do método são avaliados da esquerda para a direita, ou seja,
*v.index(1)
são avaliados primeiro. Isso resulta emusize
ae o empréstimo compartilhado temporário dev
pode ser liberado novamente. Então, o receptor deindex_mut()
é avaliado, ou seja,v
é mutuamente emprestado. Isso funciona bem, pois o empréstimo compartilhado já foi finalizado e toda a expressão passa no verificador de empréstimo.Observe que a versão que compila apenas o faz desde a introdução de "vidas úteis não-lexicais". Nas versões anteriores do Rust, o empréstimo compartilhado permaneceria até o final da expressão e resultaria em um erro semelhante.
A solução mais limpa na minha opinião é usar uma variável temporária:
fonte
*v.index_mut(*v.index_mut(1)) = 999;
falha com "não pode pedir emprestado v tão mutável mais de uma vez" ~> o compilador não deveria ser, pois é*v.index_mut(*v.index(1)) = 999;
capaz de descobrir que o empréstimo interno não é mais necessário?*v.index(1)
é o valor armazenado nesse índice e esse valor não requer para manterv
vivo o empréstimo . O resultado de*v.index_mut(1)
, por outro lado, é uma expressão mutável de lugar que, teoricamente, poderia ser atribuída, mantendo assim o empréstimo vivo. Na superfície, deve ser possível ensinar ao verificador de empréstimo que uma expressão de lugar no contexto de expressão de valor pode ser tratada como uma expressão de valor; portanto, é possível que isso seja compilado em alguma versão futura do Rust.{ let index = *Index::index(&v, 1); let value = 999; *IndexMut::index_mut(&mut v, index) = value; }