У меня есть объект, который я хочу разделить на две части с помощью изменяемого заимствования, а затем объединить их вместе в исходный объект, когда разделенные ссылки выходят за пределы области видимости.
Упрощенный пример ниже предназначен для структуры Count
, содержащей один i32
, который мы хотим разделить на два &mut i32
, которые оба включаются обратно в исходный Count
, когда две изменяемые ссылки выходят за пределы области видимости.
Подход, который я использую ниже, заключается в использовании промежуточного объекта CountSplit
, который содержит изменяемую ссылку на исходный объект Count
и имеет черту Drop
, реализованную для выполнения логики повторного комбинирования.
Такой подход кажется неуклюжим. В частности, это неудобно:
let mut ms = c.make_split();
let (x, y) = ms.split();
Выполнение этого в одной строке, например let (x, y) = c.make_split().split();
, недопустимо, потому что промежуточный объект должен иметь более длительный срок службы. В идеале я мог бы сделать что-то вроде let (x, y) = c.magic_split();
и вообще не раскрывать промежуточный объект.
Есть ли способ сделать это, при котором не нужно делать два let
каждый раз, или какой-то другой способ справиться с этим шаблоном, который был бы более идиоматическим?
#[derive(Debug)]
struct Count {
val: i32,
}
trait MakeSplit<'a> {
type S: Split<'a>;
fn make_split(&'a mut self) -> Self::S;
}
impl<'a> MakeSplit<'a> for Count {
type S = CountSplit<'a>;
fn make_split(&mut self) -> CountSplit {
CountSplit {
top: self,
second: 0,
}
}
}
struct CountSplit<'a> {
top: &'a mut Count,
second: i32,
}
trait Split<'a> {
fn split(&'a mut self) -> (&'a mut i32, &'a mut i32);
}
impl<'a, 'b> Split<'a> for CountSplit<'b> {
fn split(&mut self) -> (&mut i32, &mut i32) {
(&mut self.top.val, &mut self.second)
}
}
impl<'a> Drop for CountSplit<'a> {
fn drop(&mut self) {
println!("custom drop occurs here");
self.top.val += self.second;
}
}
fn main() {
let mut c = Count { val: 2 };
println!("{:?}", c); // Count { val: 2 }
{
let mut ms = c.make_split();
let (x, y) = ms.split();
println!("split: {} {}", x, y); // split: 2 0
// each of these lines correctly gives a compile-time error
// c.make_split(); // can't borrow c as mutable
// println!("{:?}", c); // or immutable
// ms.split(); // also can't borrow ms
*x += 100;
*y += 5000;
println!("split: {} {}", x, y); // split: 102 5000
} // custom drop occurs here
println!("{:?}", c); // Count { val: 5102 }
}
DerefMut
в кортеж, еслиCountSplit
просто хранит(i32, i32)
, где первый является копией оригинала. - person nnnmmm   schedule 15.06.2020DerefMut
- интересная мысль, но я не уверен, как она решает мою проблему. Дело в том, чтобы разделить на две отдельные изменяемые ссылки, чтобы, например, я мог вызвать функцию с подписьюfn foo(a: &mut i32, b: &mut i32);
- person Dan R   schedule 15.06.2020split
возвращать две ссылки, возможно, вы могли бы заставить его вызывать закрытие с параметрамиx
иy
: площадка - person Jmb   schedule 16.06.2020