如何组成生锈功能?

Seu*_*ege 65 functional-programming rust

我正在尝试编写一个组成两个函数的函数,初始设计非常简单,它是一个函数,它接受两个函数并返回一个组合函数,然后我可以用其他函数组合,(因为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)))
}
Run Code Online (Sandbox Code Playgroud)

我想怎么用它:

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));
}
Run Code Online (Sandbox Code Playgroud)

rustc不喜欢那样,无论我尝试什么,特质界限永远不会满足.错误是:

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  | | }
   | |_^
Run Code Online (Sandbox Code Playgroud)

Jan*_*ner 106

正如@ljedrz指出的那样,为了使它工作,你只需要再次引用组合函数:

let finally = compose(&*add_and_multiply, &*divide_and_subtract);
Run Code Online (Sandbox Code Playgroud)

(请注意,在Rust中,约定规定变量名称应为snake_case)


但是,我们可以做得更好!

从Rust 1.26开始,我们可以使用抽象返回类型(以前以gated为特色#![feature(conservative_impl_trait)]).这可以帮助您大大简化示例,因为它允许您跳过生命周期,引用,Sized约束和Boxes:

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 add_and_multiply = compose(|x| x * 2, |x| x + 2);
    let divide_and_subtract = compose(|x| x / 2, |x| x - 2);

    let finally = compose(add_and_multiply, divide_and_subtract);
    println!("Result is {}", finally(10));
}
Run Code Online (Sandbox Code Playgroud)

最后,既然你提到了休息参数,我怀疑你真正想要的是有一种方法可以灵活地链式组合你想要的任意数量的函数.我为此编写了这个宏:

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));
}
Run Code Online (Sandbox Code Playgroud)

  • 根据口味的不同,可能需要在参数位置使用“ impl Trait”来进一步简化事情。 (7认同)

lje*_*drz 12

只需添加引用finally,它就可以工作:

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));
}
Run Code Online (Sandbox Code Playgroud)

解除引用addAndMultiplydivideAndSubtract揭示不是的特征对象Sized; 它需要被包装在一个Box或被引用,以便将它传递给具有Sized约束的函数.