¿Por qué múltiples referencias mutables hacen que sea imposible asignarse un miembro de estructura a sí mismo? [duplicar]
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.borrow
tiene la intersección de 'b
y, 'c
por 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
Tendrá que echar un vistazo a sus vidas 'b
y '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
, 'c
debe sobrevivir 'b
, es decir, siempre que 'b
sea válido, también lo es 'c
. Aunque, fundamentalmente, no al revés.
Como muestra, el compilador requiere 'b
y 'c
tener la misma vida útil. ¿Por qué es esto?
Echemos un vistazo a nuestras posibilidades:
'c
y'b
no tienen relación: es fácil ver que sin ninguna relación, el compilador no puede garantizar nada sobre lo que se introduceA.borrow
y, como tal, no lo permitirá.'c
Sobrevive estrictamente'b
, es decir, algunos lugares'c
es válido'b
no lo es: se
a.borrow = a.borrow
convierte en un renacimiento dela
uso de la'b
vida. Esta respuesta explica por qué sucede eso . Sin embargo, esto significa quea
ahora depende de la'b
vida útil, lo cual no es válido por una parte del tiempo quea
es válido (ya quea
tiene la vida útil'c
). Esto da el error.'b
estrictamente 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: 'b
inferido 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;
}