Rust中递归类型结合Rc的嵌套模式匹配可行性及求导程序移植问题
解决Rust中Rc包裹递归表达式的嵌套模式匹配问题
你猜得没错,这确实是Rust模式匹配的一个限制——当前稳定版里,没办法直接在模式里对Rc这类智能指针做嵌套的解引用匹配。你代码里的(f, Add(Int(n), g))这类模式之所以总是失败,是因为f和g都是Rc<Expr2>类型,而Add是Expr2的枚举变体,Rust不会自动帮你穿透Rc去匹配内部的枚举值。
怎么解决这个问题?
最直接的办法是手动解引用Rc之后再做模式匹配。因为Rc是共享所有权的智能指针,我们只需要获取内部值的不可变引用就能做匹配(毕竟表达式化简是只读操作),可以用&*rc或者rc.as_ref()来拿到内部Expr2的引用。
比如修改你的add函数,改成这样:
fn add(f: Expr, g: Expr) -> Expr { match (&*f, &*g) { (Int(m), Int(n)) => Rc::new(Int(m + n)), (Int(0), _) => g, (_, Int(n)) => add(g, f), (_, Add(Int(n), inner_g)) => add(Rc::new(Int(*n)), add(f, Rc::clone(inner_g))), (Add(inner_f, inner_g), _) => add(Rc::clone(inner_f), add(Rc::clone(inner_g), g)), _ => Rc::new(Add(f, g)), } }
这里要注意几点:
- 匹配时用
&*f拿到Expr2的不可变引用,这样就能匹配到Int、Add这些变体了 - 构造新的
Expr时要用Rc::new把枚举值包起来 - 对于需要复用的内部表达式节点,用
Rc::clone来增加引用计数,避免不必要的内存复制
同理,你的mul、pow、d等函数都需要做类似的修改,把对Expr的直接匹配改成对&*Expr的匹配。
关于你提到的“box语法”
你说的那个规划中的模式匹配解引用语法(比如box Add(...))确实存在,但它目前只针对Box类型,而且还处于不稳定状态(需要启用box_patterns特性)。更关键的是,它并不支持Rc,所以哪怕这个特性稳定了,也帮不上你当前的场景。
顺便提几个代码里的小问题
除了模式匹配的问题,你的代码还有几个编译错误需要修正:
d函数里的Var(y)分支:你直接返回了字符串x或y,但函数返回值是Expr,应该改成Rc::new(Var(x.clone()))或者Rc::new(Var(y))string_of_expr函数:你只调用了count(f).to_string()但没有返回它,应该改成count(f).to_string()作为返回值nest函数:你写了f(x),但f是Expr类型(数据结构)不是函数,应该把nest的参数改成接受一个函数,比如:fn nest(n: i32, func: impl Fn(Expr) -> Expr, x: Expr) -> Expr { if n == 0 { x } else { nest(n - 1, func, func(x)) } }main函数里的x:你直接用字符串"x"调用pow,但pow需要Expr类型,应该改成let x = Rc::new(Var("x".to_string()));,再传递给pow
内容的提问来源于stack exchange,提问作者J D
相关产品推荐
相关产品推荐

