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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 00:39:04