如何在 Rust 中实现多级柯里化函数?

问题描述 投票:0回答:2

我尝试实现类似于 Rust 中的函数式编程术语的柯里化函数

fn add_origin(x: i32) -> impl Fn(i32) -> i32 {
    return move |y| {
        x + y
    };
}

fn main() {
    let add5 = add_origin(5);
    println!("Call closure: {}", add5(6));
}

这可行,但如果我再深一层:

fn add(x: i32) -> impl Fn(i32) -> impl Fn(i32) -> i32 {
    return move |y: i32| {
        return move |z: i32| {
            x + y + z
        }
    };
}

fn main() {
    let add5 = add(5);
    let add5_10 = add5(10);
    println!("Call closure: {}", add5_10(6));
}

编译器不接受并告诉我:

error[E0562]: `impl Trait` not allowed outside of function and inherent method return types
 --> src/main.rs:7:35
  |
7 | fn add(x: i32) -> impl Fn(i32) -> impl Fn(i32) -> i32 {
  |                                   ^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error

为什么这是不允许的? Rust 有更好的选择吗?

function rust functional-programming closures currying
2个回答
9
投票

impl Trait
语法只能用在函数签名的参数位置或返回位置。这两个都很好:

fn takes_fn(fn_arg: impl Fn(i32) -> i32) {}

fn returns_fn() -> impl Fn(i32) -> i32 {
  |x| x
}

但是这些都不起作用:

fn takes_fn(fn_arg: impl Fn(i32) -> impl Fn(i32) -> i32) {}

fn returns_fn() -> impl Fn(i32) -> impl Fn(i32) -> i32 {
  |x| x
}

因为第二个嵌套

impl Trait
不再位于参数或返回位置,但现在是参数或返回类型签名的一部分,这是不允许的。基本上,多个嵌套的
impl Trait
永远不会工作,无论你将它们放在哪里,并且你总是会得到相同的编译错误。

好消息是,这不会阻止您完成您想要完成的事情,因为

impl Trait
只是语法糖,它的使用是可选的。在您的特定示例中,我们可以使用盒装特征对象来返回您的柯里化函数。

fn add(x: i32) -> impl Fn(i32) -> Box<dyn Fn(i32) -> i32> {
    move |y: i32| {
        Box::new(move |z: i32| {
            x + y + z
        })
    }
}

fn main() {
    let add5 = add(5);
    let add5_10 = add5(10);
    println!("Call closure: {}", add5_10(6)); // prints "Call closure: 21"
}

游乐场

另请参阅:


0
投票

现在有一个解决方案,使用夜间功能

type_alias_impl_trait
,如这篇好文章中所写(然后继续在 proc 宏中自动柯里化):https://peppe.rs/posts/auto- currying_rust_functions/#fn2

将帖子中的方法调整为我们得到的问题中的代码:

#![feature(type_alias_impl_trait)]  // allows us to use `impl Fn` in type aliases!

type T0 = i32;                 // the return value when zero args are to be applied
type T1 = impl Fn(i32) -> T0;  // the return value when one arg is to be applied
type T2 = impl Fn(i32) -> T1;  // the return value when two args are to be applied


fn add(x: i32) -> T2 {
    return move |y: i32| {
        return move |z: i32| {
            x + y + z
        }
    };
}

fn main() {
    let add5 = add(5);
    let add5_10 = add5(10);
    println!("Call closure: {}", add5_10(6));
}

您可以在playground

中使用此代码
© www.soinside.com 2019 - 2024. All rights reserved.