我有一个表达式,它创建的变量是双引用的,只是为了在调用中使用它而必须取消对变量的引用。我怀疑有一种更简单的语法和方法来调用我的函数。
我有两种类型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
,解构为a
和b
,从而使事情简单化?我是否可以使用更少的&号和mut
s和derf ?注意,我不需要a
是可变的,只需要b
。
忽略&mut C
,因为它是必需的,如果您为每个&符号&
计算一分,为每个mut
计算一分,为每个deref*
计算一分,那么您将获得8分。得分较低的解决方案;是赢家。
我不认为有任何方法可以简化函数的签名。您可以将Vec<T>
替换为[T]
,前提是您不需要从矢量中推入或弹出元素,但此更改不会影响"分数"。根据你的定义。
由于匹配的人机工程学,a
和b
都变成了双引用。
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
是"避免a
和b
双引用的方法。正如你所看到的,它不是很漂亮。首先,我在元组前面添加了&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中添加/删除项。