代码之家  ›  专栏  ›  技术社区  ›  Web3 Philosopher

如何在Rust中组合函数?

  •  82
  • Web3 Philosopher  · 技术社区  · 9 年前

    我试图写一个由两个函数组成的函数。最初的设计非常简单:一个函数接受两个函数并返回一个组合函数,然后我可以与其他函数组合,因为Rust没有rest参数。我遇到了一堵墙,墙上堆满了令人沮丧的无用编译器错误。

    fn compose<'a, A, B, C, G, F>(f: F, g: G) -> Box<Fn(A) -> C + 'a>
    where
        F: 'a + Fn(A) -> B + Sized,
        G: 'a + Fn(B) -> C + Sized,
    {
        Box::new(move |x| g(f(x)))
    }
    

    我想如何使用它:

    fn main() {
        let addAndMultiply = compose(|x| x * 2, |x| x + 2);
        let divideAndSubtract = compose(|x| x / 2, |x| x - 2);
    
        let finally = compose(*addAndMultiply, *divideAndSubtract);
        println!("Result is {}", finally(10));
    }
    

    编译器不喜欢这样,无论我尝试什么,特征边界永远不会满足。错误是:

    error[E0277]: the size for values of type `dyn std::ops::Fn(_) -> _` cannot be known at compilation time
      --> src/main.rs:13:19
       |
    13 |     let finally = compose(*addAndMultiply, *divideAndSubtract);
       |                   ^^^^^^^ doesn't have a size known at compile-time
       |
       = help: the trait `std::marker::Sized` is not implemented for `dyn std::ops::Fn(_) -> _`
       = note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
    note: required by `compose`
      --> src/main.rs:1:1
       |
    1  | / fn compose<'a, A, B, C, G, F>(f: F, g: G) -> Box<Fn(A) -> C + 'a>
    2  | | where
    3  | |     F: 'a + Fn(A) -> B + Sized,
    4  | |     G: 'a + Fn(B) -> C + Sized,
    5  | | {
    6  | |     Box::new(move |x| g(f(x)))
    7  | | }
       | |_^
    
    3 回复  |  直到 7 年前
        1
  •  2
  •   Kirill A. Khalitov    5 年前

    像 @ljedrz points out ,要使其工作,您只需再次引用组合函数:

    let finally = compose(&*multiply_and_add, &*divide_and_subtract);
    


    然而,我们可以让它变得更好!

    由于锈蚀1.26,我们可以使用 abstract return types (之前的特色是 #![feature(conservative_impl_trait)] ). 这可以帮助您大大简化示例,因为它允许您跳过生命周期、引用、, Sized Box

    fn compose<A, B, C, G, F>(f: F, g: G) -> impl Fn(A) -> C
    where
        F: Fn(A) -> B,
        G: Fn(B) -> C,
    {
        move |x| g(f(x))
    }
    
    fn main() {
        let multiply_and_add = compose(|x| x * 2, |x| x + 2);
        let divide_and_subtract = compose(|x| x / 2, |x| x - 2);
    
        let finally = compose(multiply_and_add, divide_and_subtract);
        println!("Result is {}", finally(10));
    }
    

    macro_rules! compose {
        ( $last:expr ) => { $last };
        ( $head:expr, $($tail:expr), +) => {
            compose_two($head, compose!($($tail),+))
        };
    }
    
    fn compose_two<A, B, C, G, F>(f: F, g: G) -> impl Fn(A) -> C
    where
        F: Fn(A) -> B,
        G: Fn(B) -> C,
    {
        move |x| g(f(x))
    }
    
    fn main() {
        let add = |x| x + 2;
        let multiply = |x| x * 2;
        let divide = |x| x / 2;
        let intermediate = compose!(add, multiply, divide);
    
        let subtract = |x| x - 2;
        let finally = compose!(intermediate, subtract);
    
        println!("Result is {}", finally(10));
    }
    
    推荐文章