You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在检查同一变量的循环中使用可变捕获变量的闭包?

没问题,咱们来解决这个Rust借用检查器的小麻烦!先看看你给出的原代码和错误信息:

原代码

fn main() { 
    let mut foo = 1; 
    let mut func = || foo += 1; 
    while foo < 5 { 
        func(); 
    } 
}

报错信息

error[E0503]: cannot use `foo` because it was mutably borrowed 
--> src/main.rs:5:11 
| 4 | let mut func = || foo += 1; 
| -- borrow of `foo` occurs here 
5 | while foo < 5 { 
| ^^^ use of borrowed `foo`

问题根源

这段代码报错的核心是Rust的独占可变借用规则:闭包func捕获了foo的可变引用,一旦这个借用存在,你就不能在其他地方(比如while循环的条件判断里)再访问foo——可变借用是独占的,编译器不允许这种“一边持有可变借用,一边读取原变量”的操作。

绕过借用检查器的合法方案

下面是几种不需要改用普通函数的替代方案,都能安全实现你的需求:

1. 使用Cell(适合Copy类型变量)

如果你的变量是整数、布尔值这类Copy类型,Cell是最轻量的选择。它通过直接拷贝值实现内部可变性,没有运行时借用检查开销:

use std::cell::Cell;

fn main() {
    let foo = Cell::new(1);
    // 闭包无需mut,直接捕获Cell
    let func = || foo.set(foo.get() + 1);
    
    while foo.get() < 5 {
        func();
    }
    
    println!("foo最终的值是: {}", foo.get()); // 输出5
}

2. 使用RefCell(适合非Copy类型或多变量修改)

如果要修改的变量不是Copy类型,或者需要同时修改多个变量,RefCell会更合适。它通过运行时借用检查保证内存安全(逻辑错误时会panic,但正常使用完全安全):

use std::cell::RefCell;

// 把需要修改的多个变量封装到结构体中
struct AppState {
    counter: i32,
    total: i32,
}

fn main() {
    let state = RefCell::new(AppState { counter: 1, total: 0 });
    
    let mut func = || {
        // 获取可变引用修改内部值
        let mut s = state.borrow_mut();
        s.counter += 1;
        s.total += s.counter;
    };
    
    while state.borrow().counter < 5 {
        func();
    }
    
    let final_state = state.borrow();
    println!("counter: {}, total: {}", final_state.counter, final_state.total);
    // 输出: counter: 5, total: 14
}

3. 手动传递可变引用(无内部可变性)

如果你不想用标准库的内部可变性容器,也可以把变量封装到结构体里,每次调用闭包时手动传递可变引用。这种方式完全依赖编译期检查,没有运行时开销:

struct State {
    foo: i32,
}

fn main() {
    let mut state = State { foo: 1 };
    
    // 闭包接收可变引用作为参数
    let mut func = |s: &mut State| {
        s.foo += 1;
    };
    
    while state.foo < 5 {
        func(&mut state);
    }
    
    println!("foo最终的值是: {}", state.foo); // 输出5
}

总结

  • 单线程场景下,Cell和RefCell是最常用的“绕过”编译期借用检查的合法方式——它们并没有打破Rust的内存安全规则,只是把借用检查从编译期移到了运行时。
  • 如果要修改多个变量,把它们封装到结构体里再配合RefCell使用,代码会更清晰易维护。

内容的提问来源于stack exchange,提问作者bertfred

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 08:23:12