Por que várias referências mutáveis tornam impossível atribuir um membro de struct a si mesmo? [duplicado]
Não posso pedir emprestado onde pensei que poderia. Reduzi o problema a este caso:
struct A<'a> {
borrow: &'a mut u8,
}
fn does_nothing<'b, 'c>(a: &'b mut A<'c>) {
a.borrow = a.borrow;
}
error[E0623]: lifetime mismatch
--> src/lib.rs:6:16
|
5 | fn does_nothing<'b, 'c>(a: &'b mut A<'c>) {
| -------------
| |
| these two types are declared with different lifetimes...
6 | a.borrow = a.borrow;
| ^^^^^^^^ ...but data from `a` flows into `a` here
Parece que a.borrow
tem a interseção de 'b
e, 'c
portanto, não pode ser garantido que ainda terá o tempo de vida 'c
.
Não tenho nenhum problema real com isso e posso contornar isso tornando as duas vidas iguais, mas por que isso não pede cheque emprestado?
Parece que as estruturas não são importantes para mostrar esse problema e empréstimos duplos mostram isso facilmente.
Eu tenho três funções que são muito semelhantes, mas eu teria problemas em saber qual compila e qual erro as funções não compiladas causariam.
A função genérica simples:
fn only_borrow<T>(a: &mut T) {
*a = *a;
}
resulta no erro:
error[E0507]: cannot move out of `*a` which is behind a mutable reference
--> src/lib.rs:2:10
|
2 | *a = *a;
| ^^ move occurs because `*a` has type `T`, which does not implement the `Copy` trait
Incluir um nível extra de indireção muda o erro
fn only_borrow_double<T>(a: &mut &mut T) {
*a = *a;
}
error[E0623]: lifetime mismatch
--> src/lib.rs:2:10
|
1 | fn only_borrow_double<T>(a: &mut &mut T) {
| -----------
| |
| these two types are declared with different lifetimes...
2 | *a = *a;
| ^^ ...but data from `a` flows into `a` here
Mudar as vidas úteis implícitas pode corrigir o erro:
fn working_double<'b, T>(a: &'b mut &'b mut T) {
*a = *a;
}
Respostas
Você terá que dar uma olhada em suas vidas 'b
e 'c
:
&'b mut ...
significa que você tem uma referência que está viva e válida por um "tempo"'b
.A<'c>
significa que você tem um objeto que está ativo e para o qual é válido'c
.
O que você não tem é uma relação específica entre essas duas vidas. A única coisa que o compilador pode deduzir é que, uma vez que A<'c>
está atrás de um &'b
, 'c
deve sobreviver 'b
, ou seja, sempre que 'b
for válido, será 'c
. Embora, crucialmente, não o contrário.
Como você mostra, o compilador requer 'b
e 'c
deve ter o mesmo tempo de vida. Por que é isso?
Vamos dar uma olhada em nossas possibilidades:
'c
e'b
não ter relação: É fácil perceber que sem relação, o compilador nada pode garantir sobre o que se põeA.borrow
e como tal não o permite.'c
estritamente outlives'b
, ou seja, alguns lugares'c
é válido'b
não é:
a.borrow = a.borrow
torna - se um renascimento dea
usar a'b
vida. Essa resposta explica por que isso acontece . No entanto, isso significa quea
agora é dependente do'b
tempo de vida, que não é válido por parte do tempoa
é válido (já quea
tem o tempo de vida'c
). Isso dá o erro.'b
estritamente sobrevive'c
: Se tivéssemos essa relação, poderia funcionar. O novo amanhã será válido, já que obteremos um tempo de vida "maior" ('b
) do que solicitamos ('c
). No entanto, já'c: 'b
inferimos pelo compilador nos bastidores. Portanto, adicionar esta vida significa que as duas vidas se tornam iguais e estamos de volta ao ponto de partida:
struct A<'a> {
borrow: &'a mut u8,
}
/// We add the relation 'b: 'c
fn does_nothing<'b: 'c, 'c>(a: &'b mut A<'c>) {
a.borrow = a.borrow;
}