我可以简化使用双引用的元组表达式吗?



我有一个表达式,它创建的变量是双引用的,只是为了在调用中使用它而必须取消对变量的引用。我怀疑有一种更简单的语法和方法来调用我的函数。

我有两种类型A和B,都不能移动,但都是可克隆的。我正在调用具有a和B元组的Vec的函数,需要索引Vec以取出元组,将元组中的值解构为局部变量,并在另一个函数调用中不可变地使用一个值,但另一个值可变。

pub fn my_func(v: &mut Vec<(&A, &mut B)>, x: &mut C) {
let i = 0_usize;        // Would be a loop index normally.
let (a, b) = &mut v[i]; // This makes b a double reference.

x.do_something(*b);     // This expects &mut B as parameter.
}

如何将签名改为my_func,索引改为v,解构为ab,从而使事情简单化?我是否可以使用更少的&号和muts和derf ?注意,我不需要a是可变的,只需要b

忽略&mut C,因为它是必需的,如果您为每个&符号&计算一分,为每个mut计算一分,为每个deref*计算一分,那么您将获得8分。得分较低的解决方案;是赢家。

我不认为有任何方法可以简化函数的签名。您可以将Vec<T>替换为[T],前提是您不需要从矢量中推入或弹出元素,但此更改不会影响"分数"。根据你的定义。

由于匹配的人机工程学,ab都变成了双引用。

let左侧的图案没有完全相同的"形状"。就像右边的表达式一样,编译器会"修复"。给你的。表达式类型为&mut (&A, &mut B)。该模式与外部的&mut不匹配,因此编译器将其推入元组中,给出(&mut &A, &mut &mut B)。现在形状匹配:外部类型是一个2元组,所以a&mut &A,b&mut &mut B

我对你的功能提出了一些变化:

pub struct A;
pub struct B;
pub struct C;
impl C {
fn do_something(&mut self, b: &mut B) {}
}
pub fn my_func_v1(v: &mut Vec<(&A, &mut B)>, x: &mut C) {
let i = 0_usize;
let (a, b) = &mut v[i];
x.do_something(b);
}
pub fn my_func_v2(v: &mut Vec<(&A, &mut B)>, x: &mut C) {
let i = 0_usize;
let &mut (a, &mut ref mut b) = &mut v[i];
x.do_something(b);
}
pub fn my_func_v2a(v: &mut Vec<(&A, &mut B)>, x: &mut C) {
let i = 0_usize;
let (a, &mut ref mut b) = v[i];
x.do_something(b);
}
pub fn my_func_v3(v: &mut Vec<(&A, &mut B)>, x: &mut C) {
let i = 0_usize;
let (a, b) = &mut v[i];
let (a, b) = (*a, &mut **b);
// Or:
//let a = *a;
//let b = &mut **b;
x.do_something(b);
}
pub fn my_func_v4(v: &mut Vec<(&A, &mut B)>, x: &mut C) {
let i = 0_usize;
let e = &mut v[i];
let (a, b) = (e.0, &mut *e.1);
// Or:
//let a = e.0;
//let b = &mut *e.1;
x.do_something(b);
}

v1是相同的,除了我写了b而不是*b。编译器看到类型为&mut &mut B的表达式和类型为&mut B的形参,就会透明地对外部引用解引用。如果参数类型是泛型(这里是非泛型的&mut B),这可能不起作用。

v2是"避免ab双引用的方法。正如你所看到的,它不是很漂亮。首先,我在元组前面添加了&mut,以匹配右侧表达式的类型,以防止匹配人体工程学。接下来,我们不能只写b,因为编译器将此模式解释为移动/复制,但我们不能移出可变引用,而且&mut T不是Copy&mut ref mut b是reborrow的模式版本。

v2a就像v2,但是两边都去掉了&mut(感谢loganfsmyth的提醒!)v[i]的类型是(&A, &mut B),所以它不能被移动,但是它是一个左值,所以如果我们重新引用它不能被移动/复制的部分,那么整个东西根本不会被移动。

记住,在模式中,&mut解构引用,而ref mut构造引用。现在,&mut ref mut可能看起来像一个noop,但它不是。双可变引用&mut &mut T实际上有两个不同的生命周期;让我们将它们命名为'a作为内部生命周期,'b作为外部生命周期,给出&'b mut &'a mut T('b'a短)。当您解引用这样的值时(使用*运算符或&mut模式),输出是&'b mut T,而不是&'a mut T。如果它是&'a mut T,那么您最终可能会有多个对同一内存位置的可变引用。b生成&'a mut T,而&mut ref mut b生成&'b mut T

v3使用解引用操作符而不是&mut模式,这希望更容易理解。不幸的是,我们需要显式地重新借用(&mut **b);*b被解释为从可变引用中移出。

b&mut &mut B时,我们将b*b传递给do_something,这两者实际上都不是"正确的"。正确的表达式为&mut **b。但是,编译器会在函数/方法调用中自动引用和解引用参数(包括接收者),但不会在其他上下文中(例如局部变量的初始化项)。

v4通过依赖于使用.操作符的自动取消来节省一对*

一种选择是显式地将b绑定声明为直接对v[i]的引用。这可以用

来完成
let (a, ref mut b) = v[i];
x.do_something(b);

为了简化签名,你有很大的限制,但一个地方可以从

开始
pub fn my_func(v: &mut [(&A, &mut B)], x: &mut C) {

假设您的函数实际上没有尝试在vector中添加/删除项。

最新更新