¿Por qué múltiples referencias mutables hacen que sea imposible asignarse un miembro de estructura a sí mismo? [duplicar]

Aug 17 2020

No puedo pedir prestado donde pensé que podría. Reduje el 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.borrowtiene la intersección de 'by, 'cpor lo tanto, no se puede garantizar que aún tenga la vida útil 'c.

No tengo ningún problema real con esto y puedo solucionarlo haciendo que ambas vidas sean iguales, pero ¿por qué esto no toma un cheque prestado?


Parece que las estructuras no son importantes para mostrar este problema y los préstamos dobles lo muestran fácilmente.

Tengo tres funciones que son bastante similares, pero tendría problemas para saber cuál compila y qué error darían las que no compilan.

La función genérica simple:

fn only_borrow<T>(a: &mut T) {
    *a = *a;
}

da como resultado el error:

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 un nivel adicional de indirección cambia el error

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

El cambio de las vidas útiles implícitas puede corregir el error:

fn working_double<'b, T>(a: &'b mut &'b mut T) {
    *a = *a;
}

Respuestas

Emoun Aug 18 2020 at 09:59

Tendrá que echar un vistazo a sus vidas 'by 'c:

  • &'b mut ...significa que tiene una referencia que está en vivo y es válida por un "tiempo" 'b.
  • A<'c>significa que tiene un objeto que está activo y es válido 'c.

Lo que no tienes es una relación específica entre estas dos vidas. Lo único que puede deducir el compilador es que dado que A<'c>está detrás de a &'b, 'cdebe sobrevivir 'b, es decir, siempre que 'bsea ​​válido, también lo es 'c. Aunque, fundamentalmente, no al revés.

Como muestra, el compilador requiere 'by 'ctener la misma vida útil. ¿Por qué es esto?

Echemos un vistazo a nuestras posibilidades:

  1. 'cy 'bno tienen relación: es fácil ver que sin ninguna relación, el compilador no puede garantizar nada sobre lo que se introduce A.borrowy, como tal, no lo permitirá.
  2. 'cSobrevive estrictamente 'b, es decir, algunos lugares 'ces válido 'bno lo es: se
    a.borrow = a.borrowconvierte en un renacimiento del auso de la 'bvida. Esta respuesta explica por qué sucede eso . Sin embargo, esto significa que aahora depende de la 'bvida útil, lo cual no es válido por una parte del tiempo que aes válido (ya que atiene la vida útil 'c). Esto da el error.
  3. 'bestrictamente sobrevive 'c: si tuviéramos esta relación, podría funcionar. El nuevo mañana será válido, ya que obtenemos una vida útil "mayor" ( 'b) de la que pedimos ( 'c). Sin embargo, ya lo hemos 'c: 'binferido el compilador detrás de escena. Por lo tanto, agregar esta vida significa que las dos vidas se vuelven iguales y luego estamos de regreso donde comenzamos:
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;
}