Rust中Vec存储闭包报cannot borrow `**h` as mutable错误如何优雅解决?
报错原因
FnMut trait 的调用要求获取自身的可变借用,其核心方法签名为 fn call_mut(&mut self, args: Args) -> Output。你最初的代码中 Vec 存储的是 &dyn FnMut 不可变引用,Rust 借用规则不允许通过不可变引用修改其指向的值,因此调用闭包时会触发可变借用冲突错误。
无动态内存分配的解决方案
按适用场景从简到繁排序:
方案1:改用 Fn trait(适配当前示例,零额外开销)
你给出的示例闭包仅修改传入的参数,没有修改自身捕获的外部变量,完全符合 Fn trait 的约束。而 Fn 调用仅需要不可变借用自身,直接修改 trait 约束即可正常运行,不需要任何额外声明或分配:
fn main() { let hh: Vec<&dyn Fn(&mut i32)> = vec![&|i: &mut i32| { *i = 10; }]; let mut i = 0; let h = &hh[0]; h(&mut i); println!("{}", i); }
方案2:存储函数指针(无捕获闭包场景,无动态分发开销)
如果你的所有闭包都没有捕获外部变量,还可以直接存储函数指针,连 trait 对象的动态分发开销都没有,也不需要引用,闭包直接存在 Vec 的栈内存中:
fn main() { let hh: Vec<fn(&mut i32)> = vec![|i: &mut i32| { *i = 10; }]; let mut i = 0; let h = hh[0]; h(&mut i); println!("{}", i); }
方案3:需要 FnMut 场景的无堆分配方案
如果你确实需要使用捕获可变变量的 FnMut 闭包,又不想使用 Box<dyn FnMut> 做堆分配,可以通过宏批量处理闭包绑定,避免手动逐个声明的冗余:
// 批量构建FnMut Vec的宏,支持最多N个闭包,可根据需要扩展tuple实现长度 macro_rules! make_fnmut_vec { ($($closure:expr),*) => {{ let mut closures = ($($closure,)*); let mut vec = Vec::new(); trait AppendToVec { fn append_to(&mut self, vec: &mut Vec<&mut dyn FnMut(&mut i32)>); } impl<A: FnMut(&mut i32) + 'static> AppendToVec for (A,) { fn append_to(&mut self, vec: &mut Vec<&mut dyn FnMut(&mut i32)>) { vec.push(&mut self.0); } } impl<A: FnMut(&mut i32) + 'static, B: FnMut(&mut i32) + 'static> AppendToVec for (A, B) { fn append_to(&mut self, vec: &mut Vec<&mut dyn FnMut(&mut i32)>) { vec.push(&mut self.0); vec.push(&mut self.1); } } // 可继续扩展到3、4...个元素的tuple实现,满足实际需求 closures.append_to(&mut vec); vec }}; } fn main() { let mut count = 0; // 直接传入闭包即可,不需要手动声明每个闭包变量 let mut hh = make_fnmut_vec!( |i: &mut i32| { *i = 10; }, |i: &mut i32| { count += 1; *i += count; } ); let mut i = 0; hh[0](&mut i); println!("{}", i); // 输出10 hh[1](&mut i); println!("{}", i); // 输出11 }
这个方案所有闭包都存储在栈上的 tuple 中,没有任何堆内存分配,宏自动完成变量绑定和 trait 对象转换,写法简洁。
内容的提问来源于stack exchange,提问作者Anonymous
相关产品推荐
相关产品推荐

