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 }
复制代码

猜你喜欢

转载自juejin.im/post/7018076395741904903