Mutably borrow one struct field while borrowing another in a closure

守給你的承諾、 提交于 2019-11-26 04:28:55

问题


I have a struct containing two fields and I want to modify one field (mutable borrow) using another field (immutable borrow), but I get an error from the borrow checker.

For instance, the following code:

struct Struct {
    field1: Vec<i32>,
    field2: Vec<i32>,
}

fn main() {
    let mut strct = Struct {
        field1: vec![1, 2, 3],
        field2: vec![2, 3, 4],
    };

    strct.field1.retain(|v| !strct.field2.contains(v));

    println!(\"{:?}\", strct.field1);
}

gives the following error:

error[E0502]: cannot borrow `strct.field1` as mutable because it is also borrowed as immutable
  --> src/main.rs:12:5
   |
12 |     strct.field1.retain(|v| !strct.field2.contains(v));
   |     ^^^^^^^^^^^^^------^---^^-----^^^^^^^^^^^^^^^^^^^^
   |     |            |      |    |
   |     |            |      |    first borrow occurs due to use of `strct` in closure
   |     |            |      immutable borrow occurs here
   |     |            immutable borrow later used by call
   |     mutable borrow occurs here

What are the Rust ways of updating one field using another from within a closure?


回答1:


Usually the borrow checker can distinguish between the different fields of a structure, but this doesn't work within closures (lambdas).

Instead, borrow the second field outside the closure:

let field2 = &strct.field2;
strct.field1.retain(|v| !field2.contains(v));



回答2:


This recent blog post shows a very useful pattern for this kind of problem:

Sometimes, when I want to be very precise, I will write closures in a stylized way that makes it crystal clear what they are capturing. Instead of writing |v| ..., I first introduce a block that creates a lot of local variables, with the final thing in the block being a move closure (move closures take ownership of the things they use, instead of borrowing them from the creator). This gives complete control over what is borrowed and how. In this case, the closure might look like:

In other words, the borrows are defined right with the closure and moved into the closure. This makes it totally clear that their purpose is to provide the closure with borrowed values. In context of the original question the pattern would look like this:

strct.field1.retain({
    let field2 = &strct.field2;
    move |v| !field2.contains(v)
});

A nice property of this code is that the borrow of field2 does not stick around after it is no longer used.



来源:https://stackoverflow.com/questions/36379242/mutably-borrow-one-struct-field-while-borrowing-another-in-a-closure

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!