假设有以下结构体:
#[derive(Debug)]
struct A {
x: i32,
}
复制代码
若要设计一函数,可以改变结构体中成员x的值:
fn add(a: A, b: A) {
a.x += b.x; //编译不通过
}
复制代码
编译结果如下:
|
137 | fn add(a: A, b: A) {
| - help: consider changing this to be mutable: `mut a`
138 | a.x += b.x;
| ^^^^^^^^^^ cannot assign
复制代码
需要给参数加上mut:
fn add_mut(mut a: A, b: A) {
a.x += 1; //不但可以修改成员值
a = A { x: 1 }; //还可以修改a本身
a.x += 2; //重新赋值后仍然可以修改新值成员变量
}
let a = A { x: 1 };
let b = A { x: 2 };
add_mut(a, b);
复制代码
如果希望只修改成员x的值:
fn add_ref_mut<'s>(a: &'s mut A, b: &'s mut A) {
a.x += b.x;
a = b; // 修改本身则编译不通过
}
复制代码
编译不通过:
|
153 | fn add_ref_mut<'s>(a: &'s mut A, b: &'s mut A) {
| - help: consider making this binding mutable: `mut a`
154 | a.x += b.x;
155 | a = b; // borken
| ^^^^^ cannot assign to immutable argument
复制代码
同时,还会有一个前置条件,就是传入的参数必须为mut:
//注:第二个参数 b 没有修改,其实可以去掉mut,但这里为了一致性就保留下来了。
fn add_ref_mut<'s>(a: &'s mut A, b: &'s mut A) {
a.x += b.x;
}
let a = A { x: 1 };
let b = A { x: 2 };
add_ref_mut(&mut a, &mut b);// 不通过
复制代码
错误为:
--> src/controller/game_service.rs:184:21
|
182 | let a = A { x: 1 };
| - help: consider changing this to be mutable: `mut a`
183 | let b = A { x: 2 };
184 | add_ref_mut(&mut a, &mut b);
| ^^^^^^ cannot borrow as mutable
复制代码
修改为:
let mut a = A { x: 1 };
let mut b = A { x: 2 };
add_ref_mut(&mut a, &mut b);// 通过
复制代码
也就是说,使用mut引用,前置条件是传入参数必须声明为mut。
也可以mut加 &mut,这样所有权就不会转移,又能修改变量本身:
fn add_mut_ref<'s>(mut a: &'s mut A, b: &'s mut A) {
a.x += 1;
a = b;
}
复制代码
总结几种参数写法:
参数 | 变量可修改 | 数据可修改 | 所有权转移 | 前置mut |
---|---|---|---|---|
a: A | N | N | Y | 无 |
mut a: A | Y | Y | Y | N |
a: &A | N | N | N | 无 |
a : &mut A | N | Y | N | Y |
mut a : &mut A | Y | Y | N | Y |
注:这里的所有权转移,是基于数据类型没有Copy与Clone特性的前提下的,否则就是Copy了。
何时用mut何时用&mut?
假如结构体有Copy特性,会如何?
#[derive(Copy, Clone, Debug)]
struct B {
x: i32,
}
fn add_copy(mut a: B, x: i32) {
a.x += x;
println!("in side {:?}", a);
}
let a = B { x: 0 };
add_copy(a, 1); // 这里所有权没有转移,而是复制
add_copy(a, 3); // 这里所有权没有转移,而是复制
let mut b = a; // 这里所有权没有转移,而是复制
b.x = 2;
println!("what is a?:{:?}", a);
复制代码
运行结果是:
in side B { x: 1 }
in side B { x: 3 }
what is a?:B { x: 0 }
复制代码
可见,如果对象有Copy特性,所有权就不会转移,直接复制了。
所以,如果要对数据进行修改,最可靠的方式还是&mut
:
fn add_copy_mut(a: &mut B, x: i32) {
a.x += x;
println!("in side {:?}", a);
}
let a = B { x: 0 };
let mut a = a; // 这里也是复制,通过rust的变量遮蔽(variable shadowing),把原来的a给覆盖掉
add_copy_mut(&mut a, 5);
println!("what is a now?:{:?}", a);
复制代码
变量终于修改成功:
in side B { x: 5 }
what is a now?:B { x: 5 }
复制代码