trece - rust español
¿Cuál es la forma correcta de usar vidas con una estructura en Rust? (2)
Después de verificar con Manishearth y eddyb en el #Crust IRC, creo que no es posible que una estructura almacene una referencia a sí misma o una parte de sí misma. Entonces, lo que estás tratando de hacer no es posible dentro del sistema de tipos de Rust.
Quiero escribir esta estructura:
struct A {
b: B,
c: C,
}
struct B {
c: &C,
}
struct C;
El Bc
debe ser tomado de Ac
.
A ->
b: B ->
c: &C -- borrow from --+
|
c: C <------------------+
Esto es lo que intenté: struct C;
struct B<''b> {
c: &''b C,
}
struct A<''a> {
b: B<''a>,
c: C,
}
impl<''a> A<''a> {
fn new<''b>() -> A<''b> {
let c = C;
A {
c: c,
b: B { c: &c },
}
}
}
fn main() {}
Pero falla:
error[E0597]: `c` does not live long enough
--> src/main.rs:17:24
|
17 | b: B { c: &c },
| ^ borrowed value does not live long enough
18 | }
19 | }
| - borrowed value only lives until here
|
note: borrowed value must be valid for the lifetime ''b as defined on the method body at 13:5...
--> src/main.rs:13:5
|
13 | fn new<''b>() -> A<''b> {
| ^^^^^^^^^^^^^^^^^^^^^
error[E0382]: use of moved value: `c`
--> src/main.rs:17:24
|
16 | c: c,
| - value moved here
17 | b: B { c: &c },
| ^ value used here after move
|
= note: move occurs because `c` has type `C`, which does not implement the `Copy` trait
He leído la documentación de Rust sobre la propiedad, pero aún no sé cómo solucionarlo.
En realidad, hay más de una razón por la cual el código anterior falla. Vamos a desglosarlo un poco y explorar algunas opciones sobre cómo solucionarlo.
Primero, eliminemos lo new
e intentemos crear una instancia de A
directamente en main
, para que veas que la primera parte del problema no tiene nada que ver con el tiempo de vida:
struct C;
struct B<''b> {
c: &''b C,
}
struct A<''a> {
b: B<''a>,
c: C,
}
fn main() {
// I copied your new directly here
// and renamed c1 so we know what "c"
// the errors refer to
let c1 = C;
let _ = A {
c: c1,
b: B { c: &c1 },
};
}
esto falla con:
error[E0382]: use of moved value: `c1`
--> src/main.rs:20:20
|
19 | c: c1,
| -- value moved here
20 | b: B { c: &c1 },
| ^^ value used here after move
|
= note: move occurs because `c1` has type `C`, which does not implement the `Copy` trait
lo que dice es que si asigna c1
a c
, mueve su propiedad a c
(es decir, no puede acceder a él por más tiempo a través de c1
, solo a través de c
). Esto significa que todas las referencias a c1
ya no serán válidas. Pero tienes un &c1
todavía en alcance (en B), por lo que el compilador no puede permitirte compilar este código.
El compilador insinúa una posible solución en el mensaje de error cuando dice que el tipo C
no se puede copiar. Si pudiera hacer una copia de una C
, su código sería válido, porque asignar c1
a c
crearía una nueva copia del valor en lugar de mover la propiedad de la copia original.
Podemos hacer C
copiable cambiando su definición de esta manera:
#[derive(Copy, Clone)]
struct C;
Ahora el código de arriba funciona. Tenga en cuenta que lo que @ matthieu-m comenta sigue siendo cierto: no podemos almacenar tanto la referencia a un valor como el valor en sí mismo en B (estamos almacenando una referencia a un valor y una COPIA del valor aquí). Sin embargo, eso no es solo por estructuras, es cómo funciona la propiedad.
Ahora, si no quiere (o no puede) hacer C
copiable, puede almacenar referencias en A
y B
lugar.
struct C;
struct B<''b> {
c: &''b C,
}
struct A<''a> {
b: B<''a>,
c: &''a C, // now this is a reference too
}
fn main() {
let c1 = C;
let _ = A {
c: &c1,
b: B { c: &c1 },
};
}
Todo bien entonces? En realidad no ... aún queremos mover la creación de A
a un new
método. Y ESO es donde nos encontraremos en problemas con nuestras vidas. Movamos la creación de A
a un método:
impl<''a> A<''a> {
fn new() -> A<''a> {
let c1 = C;
A {
c: &c1,
b: B { c: &c1 },
}
}
}
como se anticipó, aquí está nuestro error de por vida:
error[E0597]: `c1` does not live long enough
--> src/main.rs:17:17
|
17 | c: &c1,
| ^^ borrowed value does not live long enough
...
20 | }
| - borrowed value only lives until here
|
note: borrowed value must be valid for the lifetime ''a as defined on the impl at 13:1...
--> src/main.rs:13:1
|
13 | impl<''a> A<''a> {
| ^^^^^^^^^^^^^^
error[E0597]: `c1` does not live long enough
--> src/main.rs:18:24
|
18 | b: B { c: &c1 },
| ^^ borrowed value does not live long enough
19 | }
20 | }
| - borrowed value only lives until here
|
note: borrowed value must be valid for the lifetime ''a as defined on the impl at 13:1...
--> src/main.rs:13:1
|
13 | impl<''a> A<''a> {
| ^^^^^^^^^^^^^^
esto se debe a que c1
se destruye al final del new
método, por lo que no podemos devolverle una referencia.
fn new() -> A<''a> {
let c1 = C; // we create c1 here
A {
c: &c1, // ...take a reference to it
b: B { c: &c1 }, // ...and another
}
} // and destroy c1 here (so we can''t return A with a reference to c1)
Una posible solución es crear C
fuera de new
y pasarlo como parámetro:
struct C;
struct B<''b> {
c: &''b C,
}
struct A<''a> {
b: B<''a>,
c: &''a C
}
fn main() {
let c1 = C;
let _ = A::new(&c1);
}
impl<''a> A<''a> {
fn new(c: &''a C) -> A<''a> {
A {c: c, b: B{c: c}}
}
}