Rust中的函数参数可变与不可变
假设有以下结构体:
#[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 }
作者:govo
链接:https://juejin.cn/post/7018076395741904903