rust - Salir temporalmente del contenido prestado
ownership borrow-checker (2)
Estoy tratando de reemplazar un valor en un préstamo mutable; moviendo parte de él al nuevo valor:
enum Foo<T> {
Bar(T),
Baz(T),
}
impl<T> Foo<T> {
fn switch(&mut self) {
*self = match self {
&mut Foo::Bar(val) => Foo::Baz(val),
&mut Foo::Baz(val) => Foo::Bar(val),
}
}
}
El código anterior no funciona, y de manera comprensible, mover el valor fuera de
self
rompe la integridad del mismo.
Pero dado que ese valor se cae inmediatamente después, yo (si no el compilador) podría garantizar su seguridad.
¿Hay alguna forma de lograr esto? Siento que este es un trabajo para código inseguro, pero no estoy seguro de cómo funcionaría.
El código anterior no funciona, y de manera comprensible, mover el valor fuera de sí mismo rompe la integridad del mismo.
Esto no es exactamente lo que sucede aquí.
Por ejemplo, lo mismo con
self
mismo funcionaría bien:
impl<T> Foo<T> {
fn switch(self) {
self = match self {
Foo::Bar(val) => Foo::Baz(val),
Foo::Baz(val) => Foo::Bar(val),
}
}
}
El óxido está absolutamente bien con movimientos parciales y totales. El problema aquí es que no posee el valor que está tratando de mover, solo tiene una referencia prestada mutable. No puede salir de ninguna referencia, incluidas las mutables.
De hecho, esta es una de las características solicitadas con frecuencia: un tipo especial de referencia que permitiría salir de ella. Permitiría varios tipos de patrones útiles. Puedes encontrar más here y here .
Mientras tanto, en algunos casos puede usar
std::mem::replace
y
std::mem::swap
.
Estas funciones le permiten "tomar" un valor de referencia mutable, siempre que proporcione algo a cambio.
De acuerdo, descubrí cómo hacerlo con un poco de
unsafe
y
std::mem
.
Me reemplace con un valor temporal no inicializado.
Como ahora "poseo" lo que solía ser
self
, puedo mover el valor de manera segura y reemplazarlo:
use std::mem;
enum Foo<T> {
Bar(T),
Baz(T),
}
impl<T> Foo<T> {
fn switch(&mut self) {
// This is safe since we will overwrite it without ever reading it.
let tmp = mem::replace(self, unsafe { mem::uninitialized() });
// We absolutely must **never** panic while the uninitialized value is around!
let new = match tmp {
Foo::Bar(val) => Foo::Baz(val),
Foo::Baz(val) => Foo::Bar(val),
};
let uninitialized = mem::replace(self, new);
mem::forget(uninitialized);
}
}
fn main() {}