rust - 可变借入带清理的两部分
问题描述
我有一些对象,我想通过可变借用将它们分成两部分,然后在拆分引用超出范围时将它们组合回原始对象。
下面的简化示例是一个Count
包含单个 的 struct ,i32
我们希望将其拆分为两个s,当两个可变引用超出范围时&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 }
}
游乐场:
解决方案
我不认为像你这样的临时值的引用可以在今天的 Rust 中工作。
如果有任何帮助,如果您特别想调用带有两个&mut i32
参数的函数,就像您在评论中提到的那样,例如
fn foo(a: &mut i32, b: &mut i32) {
*a += 1;
*b += 2;
println!("split: {} {}", a, b);
}
如果您的链接有效,您已经可以使用与您的链接数量相同的行数来做到这一点。
使用链接,您可以调用
let (x, y) = c.make_split().split();
foo(x, y);
如果你只是省略到元组的转换,它看起来像这样:
let mut ms = c.make_split();
foo(&mut ms.top.val, &mut ms.second);
您可以通过例如将可变引用val
直接存储在CountSplit
as中来使它更漂亮一些first
,这样它就变成了foo(&mut ms.first, &mut ms.second);
. 如果你想让它感觉更像一个元组,我认为你可以使用DerefMut
to 来编写foo(&mut ms.0, &mut ms.1);
.
或者,您当然可以将其表述为采用函数的函数
impl Count {
fn as_split<F: FnMut(&mut i32, &mut i32)>(&mut self, mut f: F) {
let mut second = 0;
f(&mut self.val, &mut second);
self.val += second;
}
}
然后打电话
c.as_split(foo);
推荐阅读
- vba - 如何获取列表框选定项名称?
- javascript - 多个并发请求后我的数组显示为空
- xamarin.forms - AVPlayer 暂停时 Xamarin.iOS 本机崩溃
- javascript - 从数组中删除项目(useState Hook)
- unity3d - 在统一 3d 中创建一个碗形 3d 对象
- android - 无法在照片或图库中显示图像
- docker-compose - docker-compose.yaml 文件 - 如何在密码中表示特殊字符?
- c++ - 是否可以显式调用名称损坏函数?
- java - 在构造函数上声明日期
- google-sheets - 过滤功能中的数据按字母顺序排列