用字段借用可变结构

时间:2020-06-06 12:45:31

标签: rust

我正在尝试在函数调用中可变地借用结构及其某些成员。看到结构的第一个可变借用,(成员的)任何后续借用都标记有错误second mutable borrow occurs here

场景很简单,我们有一个保存状态的结构:

struct State {
    a: i32,
    b: i32,
    result_add: i32,
    result_subtract: i32
}

还有一个函数,该函数根据传递的成员(在我们的示例中为result_addresult_subtract)填充结构中ab的计算结果

fn do_calc(state : & mut State, var1 : &mut i32, var2: &mut i32) {
    *var1 = 4;
    *var2 = 2;

    state.result_add = state.a + state.b;
    state.result_subtract = state.a - state.b;
}

有些人为的例子,但是想法是var1var2也可以是其他结构成员。现在我们调用:

do_calc(&mut state, &mut state.a, &mut state.b);

并得到错误:

error[E0499]: cannot borrow `state.a` as mutable more than once at a time
  --> src/main.rs:24:25
   |
24 |     do_calc(&mut state, &mut state.a, &mut state.b);
   |     ------- ----------  ^^^^^^^^^^^^ second mutable borrow occurs here
   |     |       |
   |     |       first mutable borrow occurs here
   |     first borrow later used by call

我猜编译器会看到我们多次借用该结构并停止了此操作,但是如果该结构作为一个整体是可变的,那岂不是可以吗?

一种解决方案是删除结构引用并借用每个需要突变的字段:

fn do_calc(result_add : &mut i32, result_subtract : &mut i32, var1 : &mut i32, var2: &mut i32)

这可以工作,但是对于更复杂和非常奇怪的事情却很麻烦。除了以可变的方式整体借用该结构同时还借用其成员之外,还有其他选择吗?

游乐场:Original Mentioned solution

2 个答案:

答案 0 :(得分:0)

这违反了Rust and borrowing的一项基本规则,即您可以拥有一个可变引用或许多不可变引用,但不能同时使用两者。

我想编译器会看到我们多次借用该结构并停止了此操作,但是如果该结构作为一个整体是可变的,那还行吗?

如果我将整个结构借为可变的,Rust编译器将确保没有其他引用,可变的或不可变的。这就是Rust及其内存安全性的工作方式。

您可以采用多种不同的方法来构造此代码,以确保只有一个可变的引用-您已经利用了split borrowing,也许您可​​以进一步使用它并将您的结构分解为两个较小的结构结构?

struct Input {
    a: i32,
    b: i32,
}

struct Output {
    add: i32,
    subtract: i32,
}

struct State {
    input: Input,
    result: Result,
}

fn do_calc(input: &mut Input, result: &mut Output) {
    input.a = 4;
    input.b = 2;

    result.add = input.a + input.b;
    result.subtract = input.a - input.b;
}

fn main() {
    let mut state = State {
        input: Input { a: 0, b: 0 },
        result: Output {
            add: 0,
            subtract: 0,
        },
    };

    do_calc(&mut state.input, &mut state.result);

    println!("result_add {} ", state.result.add);
    println!("result_subtract {} ", state.result.subtract);
}

Playground Link

答案 1 :(得分:-2)

也许您的do_calc对一个电话负有太多责任。您可以拆分它。 Playground

struct State {
    a: i32,
    b: i32,
    result_add: i32,
    result_subtract: i32
}


fn do_calc(var1: &mut i32, var2: &mut i32) {
    *var1 = 4;
    *var2 = 3;
}

fn finish_calc(state: &mut State){
    state.result_add = state.a + state.b;
    state.result_subtract = state.a - state.b;
}


fn main() {
    let mut state = State { a:0, b:1, result_add:2, result_subtract:3 };

    {
        do_calc(&mut state.a, &mut state.b); 
        finish_calc(&mut state);
    }

}