我可以简化使用双重引用的元组表达式吗?
Can I simplify tuple expression that uses a double reference?
我有一个表达式可以创建双重引用变量,只需取消引用该变量即可在调用中使用它。我怀疑有更简单的语法和方法来调用我的函数。
我有两种类型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
和取消引用吗?请注意,我不需要 a
可变,只需 b
.
忽略 &mut C
因为它是必需的,如果你为每个和号计算一分 &
,为每个 mut
计算一分,为每个 deref 计算一分 *
,那么你得到8分。获得较少“分数”的解决方案是赢家。
我认为没有任何方法可以简化函数的签名。您可以将 Vec<T>
替换为 [T]
,前提是您不需要从向量中压入或弹出元素,但此更改不会影响您定义的“分数”。
a
和b
都因为match ergonomics而变成了双重引用。
let
左侧的模式与右侧表达式的“形状”不完全相同,因此编译器会为您“修复”它。表达式的类型是&mut (&A, &mut B)
。该模式与外部 &mut
不匹配,因此编译器将其推入元组,给出 (&mut &A, &mut &mut B)
。现在形状匹配:外部类型是一个二元组,所以 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
,以便匹配右侧表达式的类型,以防止 match ergonomics 踢进来。接下来,我们不能只写 b
因为编译器将此模式解释为 move/copy,但我们不能移出可变引用并且 &mut T
不是 Copy
。 &mut ref mut b
是再借的模式版本。
v2a
类似于 v2
,但去掉了两边的 &mut
(感谢 loganfsmyth 的提醒!)。 v[i]
是 (&A, &mut B)
类型,所以它不能移动,但它是一个左值,所以如果我们重新引用它不能是 moved/copied 的部分,那么整个事情就不会发生完全被感动了。
请记住,在模式中,&mut
解构 引用,而 ref mut
构造 引用。现在,&mut ref mut
可能看起来像个傻瓜,但事实并非如此。双可变引用 &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
。但是,编译器会在 function/method 调用中自动引用和取消引用参数(包括接收者),但不会在其他上下文中(例如局部变量的初始化程序)。
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) {
假设您的函数实际上并未尝试 add/remove 向量中的项目。
我有一个表达式可以创建双重引用变量,只需取消引用该变量即可在调用中使用它。我怀疑有更简单的语法和方法来调用我的函数。
我有两种类型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
和取消引用吗?请注意,我不需要 a
可变,只需 b
.
忽略 &mut C
因为它是必需的,如果你为每个和号计算一分 &
,为每个 mut
计算一分,为每个 deref 计算一分 *
,那么你得到8分。获得较少“分数”的解决方案是赢家。
我认为没有任何方法可以简化函数的签名。您可以将 Vec<T>
替换为 [T]
,前提是您不需要从向量中压入或弹出元素,但此更改不会影响您定义的“分数”。
a
和b
都因为match ergonomics而变成了双重引用。
let
左侧的模式与右侧表达式的“形状”不完全相同,因此编译器会为您“修复”它。表达式的类型是&mut (&A, &mut B)
。该模式与外部 &mut
不匹配,因此编译器将其推入元组,给出 (&mut &A, &mut &mut B)
。现在形状匹配:外部类型是一个二元组,所以 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
,以便匹配右侧表达式的类型,以防止 match ergonomics 踢进来。接下来,我们不能只写 b
因为编译器将此模式解释为 move/copy,但我们不能移出可变引用并且 &mut T
不是 Copy
。 &mut ref mut b
是再借的模式版本。
v2a
类似于 v2
,但去掉了两边的 &mut
(感谢 loganfsmyth 的提醒!)。 v[i]
是 (&A, &mut B)
类型,所以它不能移动,但它是一个左值,所以如果我们重新引用它不能是 moved/copied 的部分,那么整个事情就不会发生完全被感动了。
请记住,在模式中,&mut
解构 引用,而 ref mut
构造 引用。现在,&mut ref mut
可能看起来像个傻瓜,但事实并非如此。双可变引用 &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
。但是,编译器会在 function/method 调用中自动引用和取消引用参数(包括接收者),但不会在其他上下文中(例如局部变量的初始化程序)。
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) {
假设您的函数实际上并未尝试 add/remove 向量中的项目。