我应该什么时候进行闭包静音?

When should I make a closure mut?

提问人:Fajela Tajkiya 提问时间:8/10/2022 更新时间:8/10/2022 访问量:189

问:

假设我有这段代码:

let mut s = "hi".to_string();
let c = || s.push_str(" yo");
c();

它不会编译并生成此错误:

error[E0596]: cannot borrow `c` as mutable, as it is not declared as mutable                 
   --> src\test.rs:120:9
    |
119 |         let c = || s.push_str(" yo");
    |             -      - calling `c` requires mutable binding due to mutable borrow of `s`  
    |             |
    |             help: consider changing this to be mutable: `mut c`
120 |         c();
    |         ^ cannot borrow as mutable

For more information about this error, try `rustc --explain E0596`.
error: could not compile `test` due to previous error

为什么借款不能变?它不能借用什么作为可变的??为了让它工作,我必须将其更改为:c();c

let mut s = "hi".to_string();
let mut c = || s.push_str(" yo");
c();

但在这里我只是定义一个闭包.我不会修改它,比如.为什么必须将其定义为?cc = || x;let mut c

闭 包 可变 易变性

评论


答:

2赞 Jeremy Meadows 8/10/2022 #1

它可能看起来不需要可变,因为你没有修改自己,但对于闭包类型,关键字是允许它改变任何外部状态的东西。它将闭包的类型更改为 FnMut,文档将其称为“可变捕获闭包”。ccmut

2赞 Finomnis 8/10/2022 #2

这是关于所有权的。 这里并不意味着你会变异,而是你需要对闭包的-level访问权限才能执行它。mutcmut

c可变借用。这应该是相当明显的,因为调用会修改 .sc()s

现在想象一下,如果没有:cmut

let mut s = "hi".to_string();
let c = || s.push_str(" yo");

let c1 = &c;
let c2 = &c;

c1();
c2();

在这里,两者同时具有可变访问权限,根据 Rusts 借用规则,这是不可能的。编译器会自动阻止这种情况,因为它会在您从闭包内可变访问变量时立即更改闭包的类型。c1c2sFnMut

然后,您需要访问闭包才能执行它,并且情况会变成编译器错误:mut

let mut s = "hi".to_string();
let mut c = || s.push_str(" yo");

let c1 = &mut c;
let c2 = &mut c;

c1();
c2();
error[E0499]: cannot borrow `c` as mutable more than once at a time
 --> src/main.rs:5:10
  |
4 | let c1 = &mut c;
  |          ------ first mutable borrow occurs here
5 | let c2 = &mut c;
  |          ^^^^^^ second mutable borrow occurs here
6 | 
7 | c1();
  | -- first borrow later used here
5赞 Masklinn 8/10/2022 #3

但在这里我只是定义一个闭包.我不会修改它,比如.为什么必须将其定义为 let ?cc = || x;mut c

因为闭包从根本上说是一个结构,每个捕获的项都是该结构的成员。

所以你的代码大致相当于这个:

struct S<'a>(&'a mut String);
impl S<'_> {
    fn call(&mut self) {
        self.0.push_str("yo");
    }
}
fn main() {
    let mut s = "hi".to_string();
    let c = S(&mut s);
    c.call();
}

并且无法编译或多或少相同的错误:

error[E0596]: cannot borrow `c` as mutable, as it is not declared as mutable
  --> src/main.rs:14:5
   |
13 |     let c = S(&mut s);
   |         - help: consider changing this to be mutable: `mut c`
14 |     c.call();
   |     ^^^^^^^^ cannot borrow as mutable

现在你可能会反对,那是因为我定义了,但如果你不这样做,你会得到相同错误的内部部分:fn call(&mut self)

error[E0596]: cannot borrow `*self.0` as mutable, as it is behind a `&` reference
 --> src/main.rs:8:9
  |
7 |     fn call(&self) {
  |             ----- help: consider changing this to be a mutable reference: `&mut self`
8 |         self.0.push_str("yo");
  |         ^^^^^^^^^^^^^^^^^^^^^ `self` is a `&` reference, so the data it refers to cannot be borrowed as mutable

也就是说,您不能通过 .因为如果你这样做了,你可以创建多个 s,通过它你可以修改 pointee,这基本上会给你多个 .&mut&&&mut&mut

这是 Rust 的语义所不允许的:

在任何给定时间,都可以有一个可变引用,也可以有任意数量的不可变引用。