如何在Rust中实现支持FnMut的Y-Combinator?编译错误修复求助
实现支持
FnMut的Y组合子(修复FnOnce编译错误) 你碰到的问题本质是,原有的闭包捕获和Y组合子的结构没有正确适配FnMut的要求——FnMut要求闭包可以被多次可变借用,而你的实现里,闭包内部的操作消耗了捕获的变量,导致编译器只能推断它是FnOnce。下面是修复后的完整实现,以及关键修改的解释:
修复后的y_mut函数
use std::cell::RefCell; use std::rc::Rc; fn y_mut<A, O>(f: Rc<RefCell<dyn FnMut(Rc<RefCell<dyn FnMut(A) -> O>>) -> Box<dyn FnMut(A) -> O>>>) -> impl FnMut(A) -> O where A: 'static, O: 'static, { // 包装器,用来持有递归函数的引用 struct RecursiveFn<F>(Rc<RefCell<F>>); impl<F> Clone for RecursiveFn<F> { fn clone(&self) -> Self { Self(Rc::clone(&self.0)) } } impl<F: FnMut(A) -> O> RecursiveFn<F> { fn call(&mut self, arg: A) -> O { (self.0.borrow_mut())(arg) } } // 创建一个循环引用的递归结构 let rec = Rc::new(RefCell::new(None::<Box<dyn FnMut(A) -> O>>)); let f_clone = Rc::clone(&f); let rec_clone = Rc::clone(&rec); // 初始化递归函数:将自身传递给输入的f *rec.borrow_mut() = Some(Box::new(move |arg| { let mut inner_f = f_clone.borrow_mut(); // 获取递归函数的引用,传递给f生成新的闭包 let recursive_ref = RecursiveFn(rec_clone.clone()); let mut generated_fn = inner_f(Rc::new(RefCell::new(move |a| recursive_ref.call(a)))); generated_fn(arg) })); // 返回最终的递归闭包 move |arg| rec.borrow_mut().as_mut().unwrap()(arg) }
测试代码(验证递归修改数组)
fn main() { let mut a = vec![0; 5]; let n = a.len(); // 注意这里闭包返回Box<dyn FnMut(usize)>,且实现了FnMut y_mut(Rc::new(RefCell::new(|f: Rc<RefCell<dyn FnMut(usize)>>| { // 捕获a和n,返回一个FnMut闭包 Box::new(move |i| { if i < n { a[i] = i; f.borrow_mut()(i + 1); } }) })))(0); println!("a is {:?}", a); // 输出: a is [0, 1, 2, 3, 4] }
关键修改说明
- 调整返回类型为
Box<dyn FnMut>:
原实现中直接返回闭包类型,导致编译器无法推断合适的trait对象。用Box包装后,能明确告诉编译器我们需要的是一个可以被多次可变借用的闭包。 - 引入
RecursiveFn包装器:
这个结构体用来安全地持有递归函数的Rc<RefCell>引用,并且实现了Clone,保证我们可以在闭包中多次传递它,而不会消耗原始引用。 - 使用循环引用初始化递归结构:
先创建一个空的Rc<RefCell<Option<Box<dyn FnMut>>>>,然后在闭包中引用它自身,这样就形成了安全的递归引用——这是Y组合子在Rust中适配可变闭包的核心技巧,避免了原实现中闭包被一次性消耗的问题。 - 确保闭包是
FnMut:
现在输入的闭包|f| ...可以被多次调用,因为我们每次都是借用f_clone的可变引用(而非消耗它),并且返回的闭包也只是捕获了Rc克隆,没有消耗任何一次性资源。
这样修改后,你的递归闭包就可以正常作为FnMut被多次调用,完成数组的递归赋值操作啦。
内容的提问来源于stack exchange,提问作者calvin
相关产品推荐
相关产品推荐

