Por que várias referências mutáveis ​​tornam impossível atribuir um membro de struct a si mesmo? [duplicado]

Aug 17 2020

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.borrowtem a interseção de 'be, 'cportanto, 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

Emoun Aug 18 2020 at 09:59

Você terá que dar uma olhada em suas vidas 'be '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, 'cdeve sobreviver 'b, ou seja, sempre que 'bfor válido, será 'c. Embora, crucialmente, não o contrário.

Como você mostra, o compilador requer 'be 'cdeve ter o mesmo tempo de vida. Por que é isso?

Vamos dar uma olhada em nossas possibilidades:

  1. 'ce 'bnão ter relação: É fácil perceber que sem relação, o compilador nada pode garantir sobre o que se põe A.borrowe como tal não o permite.
  2. 'cestritamente outlives 'b, ou seja, alguns lugares 'cé válido 'bnão é:
    a.borrow = a.borrowtorna - se um renascimento de ausar a 'bvida. Essa resposta explica por que isso acontece . No entanto, isso significa que aagora é dependente do 'btempo de vida, que não é válido por parte do tempo aé válido (já que atem o tempo de vida 'c). Isso dá o erro.
  3. 'bestritamente 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: 'binferimos 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;
}