Rust如何实现对象方法链式调用的function composition
实现方案
直接给所有单参数可调用类型(函数、闭包)实现Compose trait即可,和你实现Pipe的思路一致,区别是compose不会立刻执行计算,而是返回一个把多个函数按顺序串联的新函数。
核心代码(支持重复调用,最常用)
这个实现基于Rust 1.75稳定的RPITIT特性,无运行时开销,适配绝大多数使用场景:
// 定义函数组合trait trait Compose<A, B>: Sized { fn compose<C, G>(self, next_fn: G) -> impl Fn(A) -> C where G: Fn(B) -> C; } // 给所有单参数、可重复调用的函数/闭包实现compose方法 impl<A, B, CurrentFn> Compose<A, B> for CurrentFn where CurrentFn: Fn(A) -> B, { fn compose<C, G>(self, next_fn: G) -> impl Fn(A) -> C where G: Fn(B) -> C, { // 捕获前后两个函数,返回新闭包:执行顺序从左到右,和pipe逻辑完全对齐 move |input| next_fn(self(input)) } }
效果验证
直接支持你需要的链式调用写法:
fn main() { // 链式组合三个函数:乘2 -> 加1 -> 转字符串 let composed_f = (|x| x * 2) .compose(|x| x + 1) .compose(|x| x.to_string()); // 传入参数才会真正执行所有组合逻辑 let result = composed_f(1); println!("{}", result); // 输出"3" // Fn约束下组合后的函数可以重复调用 let calc = (|x| x * 2).compose(|x| x + 1); assert_eq!(calc(2), 5); assert_eq!(calc(10), 21); }
按需调整约束
你可以根据使用场景替换trait里的Fn约束,适配不同的闭包类型:
- 替换为
FnOnce:支持捕获了所有权、只能调用一次的闭包,组合后的函数也仅能调用1次 - 替换为
FnMut:支持捕获了可变引用、需要修改环境的闭包,组合后的函数可以多次调用,但不支持并发调用 - 保持
Fn:仅支持不修改环境的闭包,组合后的函数可以任意多次调用、并发调用,是通用场景的最优选择
注意:当前实现的执行顺序是从左到右(链式写在前面的函数先执行),和
pipe的执行逻辑完全对齐;如果你需要传统数学定义上右结合的函数组合(即f.compose(g)等价于|x| f(g(x)),g先执行),只需要把返回闭包里的执行逻辑改成move |input| self(next_fn(input))即可。
兼容Rust 1.75以下版本的实现
如果你的工具链版本低于1.75,不支持trait中的impl Trait返回值,可以用Box包装动态分发的闭包,仅存在极小的堆分配开销:
trait Compose<A, B>: Sized { fn compose<C, G>(self, next_fn: G) -> Box<dyn Fn(A) -> C> where G: Fn(B) -> C + 'static; } impl<A, B, CurrentFn> Compose<A, B> for CurrentFn where CurrentFn: Fn(A) -> B + 'static, { fn compose<C, G>(self, next_fn: G) -> Box<dyn Fn(A) -> C> where G: Fn(B) -> C + 'static, { Box::new(move |input| next_fn(self(input))) } }
内容的提问来源于stack exchange,提问作者SmoothTraderKen
相关产品推荐
相关产品推荐

