¿Cuál es la forma idiomática más rápida para mutar múltiples campos de estructuras al mismo tiempo?
event-handling rust (1)
Muchas bibliotecas le permiten definir un tipo que implementa un trait
dado para ser utilizado como un controlador de devolución de llamada. Esto requiere agrupar todos los datos que necesitará para manejar el evento en un solo tipo de datos, lo que complica los préstamos.
Por ejemplo, mio
permite implementar Handler
y proporcionar su estructura cuando ejecuta EventLoop
. Considere un ejemplo con estos tipos de datos trivializados:
struct A {
pub b: Option<B>
};
struct B;
struct MyHandlerType {
pub map: BTreeMap<Token, A>,
pub pool: Pool<B>
}
Su manejador tiene un mapa de Token
a elementos de tipo A
Cada elemento de tipo A
puede o no tener un valor asociado de tipo B
En el controlador, quiere buscar el valor A
para un Token
dado y, si todavía no tiene un valor B
, obtener uno del Pool<B>
del manejador.
impl Handler for MyHandlerType {
fn ready(&mut self, event_loop: &mut EventLoop<MyHandlerType>,
token: Token, events: EventSet) {
let a : &mut A = self.map.get_mut(token).unwrap();
let b : B = a.b.take().or_else(|| self.pool.new()).unwrap();
// Continue working with `a` and `b`
// ...
}
}
En esta disposición, aunque es intuitivamente posible ver que self.map
y self.pool
son entidades distintas, el verificador de préstamos se queja de que self
ya está prestado (a través de self.map
) cuando accedemos a self.pool
.
Un posible enfoque para esto sería envolver cada campo en MyHandlerType
en Option<>
. Luego, al inicio de la llamada a método, take()
esos valores fuera de self
y restaurelos al final de la llamada:
struct MyHandlerType {
// Wrap these fields in `Option`
pub map: Option<BTreeMap<Token, A>>,
pub pool: Option<Pool<B>>
}
// ...
fn ready(&mut self, event_loop: &mut EventLoop<MyHandlerType>,
token: Token, events: EventSet) {
// Move these values out of `self`
let map = self.map.take().unwrap();
let pool = self.pool.take().unwrap();
let a : &mut A = self.map.get_mut(token).unwrap();
let b : B = a.b.take().or_else(|| self.pool.new()).unwrap();
// Continue working with `a` and `b`
// ...
// Restore these values to `self`
self.map = Some(map);
self.pool = Some(pool);
}
Esto funciona pero se siente un poco kluge-y. También introduce la sobrecarga de mover valores dentro y fuera de self
para cada llamada a método.
¿Cuál es la mejor manera de hacer esto?
Para obtener referencias mutables simultáneas a diferentes partes de la estructura, use la desestructuración. Ejemplo aquí .
struct Pair {
x: Vec<u32>,
y: Vec<u32>,
}
impl Pair {
fn test(&mut self) -> usize {
let Pair{ ref mut x, ref mut y } = *self;
// Both references coexist now
return x.len() + y.len();
}
}
fn main() {
let mut nums = Pair {
x: vec![1, 2, 3],
y: vec![4, 5, 6, 7],
};
println!("{}", nums.test());
}