rust - No se puede tomar prestado File from & mut self(mensaje de error: no se puede mover del contenido prestado)
borrow-checker (1)
use std::fs::File;
use std::io::Read;
pub struct Foo {
maybe_file: Option<File>,
}
impl Foo {
pub fn init(&mut self) {
self.maybe_file = Some(File::open("/proc/uptime").unwrap());
}
pub fn print(&mut self) {
let mut file = self.maybe_file.unwrap();
let mut s = String::new();
file.read_to_string(&mut s).unwrap();
println!("Uptime: {}", s);
}
}
fn main() {}
Compilar esto me dará:
error[E0507]: cannot move out of borrowed content
--> src/main.rs:14:24
|
14 | let mut file = self.maybe_file.unwrap();
| ^^^^ cannot move out of borrowed content
¿Por qué está pasando esto? ¿Qué debo hacer para resolverlo?
self
tiene el tipo &mut Foo
print
, es decir, es una referencia mutable prestada a un valor de tipo Foo
. Los tipos en Rust mueven la propiedad por defecto, es decir, tomar algo por valor invalidará estáticamente la fuente y evitará que el programador la vuelva a usar (a menos que se reinicie). En este caso, unwrap
tiene la firma:
impl Option<T> {
fn unwrap(self) -> T { ...
Es decir, toma el valor por valor de la Option
y, por lo tanto, intenta consumir la propiedad del mismo. Por lo tanto, self.maybe_file.unwrap()
está tratando de consumir los datos en maybe_file
lo que dejaría self
apuntado a datos parcialmente inválidos (es ilegal usar el campo maybe_file
después de eso). No hay forma de que el compilador pueda hacer cumplir esto con referencias prestadas que deben ser válidas siempre, ya que podrían apuntar a cualquier parte, por lo que es ilegal mudarse.
Afortunadamente, uno puede evitar este problema: el método as_ref
crea una Option<&T>
partir de una &Option<T>
y el método as_mut
crea una Option<&mut T>
de una &mut Option<T>
. La Option
resultante ya no está detrás de una referencia, por lo que es legal consumirla a través de unwrap
:
let mut file = self.maybe_file.as_mut().unwrap();
Esto difiere ligeramente porque el file
tiene el tipo &mut File
lugar de File
, pero afortunadamente &mut File
es todo lo que necesita el resto del código.
Otro enfoque para hacer que esto funcione es usar la coincidencia manual de patrones:
match self.maybe_file {
Some(ref mut file) => println!(...),
None => panic!("error: file was missing")
}
Esto está haciendo exactamente lo mismo que el .as_mut().unwrap()
solo más explícitamente: el ref mut
es crear una referencia que apunta directamente a la memoria ocupada por self.maybe_file
, al igual que as_mut
.