#closures #functional #api #no-std

no-std nclosure

提供具有分离状态和功能的可组合、可命名的闭包类型,用于API中无法使用匿名类型的情况

1个不稳定版本

0.1.0 2022年10月11日

#2756 in Rust模式

MIT 许可证

30KB
298

nclosure

nclosure 是一个库,旨在简化在无法放置匿名特质对象(如 impl)的API中使用闭包和泛型、可组合计算的使用。

特别是,它提供了一种定义闭包的便捷方法,使得闭包内嵌入的 状态 可以与原始的 fn 指针分离,该指针可以通过命名类型编码到类型系统中。

此外,它还提供了 FnOnceFnMutFn 特质的版本,可以在结构体上以自定义方式实现。这使得可以将组合计算编码为可命名的类型,本软件包提供了一些此类类型,因此允许在尚未允许匿名类型的地方将其用作返回类型。 https://github.com/rust-lang/rust/issues/29625

闭包类型

此软件包的主要功能是启用使用特质,这些特质具有作为输出提供计算的功能,或者可能需要特定、可命名的计算表示作为输入进行存储。

在此软件包中,闭包 指的是一些特定 状态 的组合,以及一个独立的 函数指针。标准的Rust闭包本质上也是如此,但每个闭包在Rust中都是一个全新的、不可命名的类型,因此很难在不进行分配/装箱/动态调度等操作的情况下将其放入统一的存储中。nclosure 软件包中的闭包是统一类型,您可以在一个闭包类型中存储不同的函数指针或状态值。

ClosureOnce

此类型是只能调用一次的闭包,它会消耗其内部状态。记住,闭包的内部状态可以很好地处理如引用之类的对象。

对于任何一次性的计算,这几乎总是您想要的。

ClosureMut

这是一个可以重复调用来生成值的闭包,会改变其内部状态——这意味着调用它需要对其具有独占引用。如果你只需要为单个计算保留引用,可以使用ClosureOnce

闭包

这包含了一个可以反复调用而不需要持有其内部状态独占引用的计算。这是闭包最一般的形式——它可以拥有自己的状态,也可以持有其他引用。

特质

由于目前无法在自定义类型上定义自己的[Fn]、FnMutFnOnce实现——例如这里定义的各种闭包类型——这个包也定义了自己的自定义计算类型,这些类型是内置类型的超集,作为解决方案(特别是,[Co]、CoMutCoOnce,这些类型自动实现了Rust核心特质)。

可以使用as_fn_onceas_fn_mutas_fn将它们自由转换为Rust核心impl-trait类型。

示例

包含此包的示例用法。

使用此包的好处是,生成的计算类型仅以内部状态、参数和返回值命名——而不是作为不透明的impl FnMut类型。

这可以带来多方面的好处。这不仅使定义闭包的存储变得更容易,还允许更容易地使用自动派生的(或其他的)特质——例如,关于线程同步和Co/CoMut/CoOnce

特别是,此特质提供的组合方法保留了泛型参数的可重复性(Co vs CoMut vs CoOnce),这意味着单个方法可以保留任意函数组合的可重复性,因为结果组合本身是一个命名类型,而不是一个匿名的impl Fn/impl FnMut/impl FnOnce类型。

闭包构造

一些示例,说明如何使用此包以更舒适的方式构造可命名的闭包。

计数器

第一个示例是构造一个生成递增值的闭包,说明构造闭包状态的简单方法。

use nclosure::prelude::*;

/// Count from the given number upwards. 
pub fn count_from(a: usize) -> ClosureMut<usize, (), usize> {
    single_closure_state(a)
        .finalise_mut(|count, ()| {
            let result = *count;
            *count += 1;
            result
        })
}


// Note that unfortunately we have to provide an empty tuple argument, because the computation traits have their arguments as a tuple.
let mut counter = count_from(5).as_fn_mut();
let first = counter(());
let second = counter(());
let third = counter(());

assert_eq!((first, second, third), (5, 6, 7))

斐波那契数列

这展示了使用流畅API构造更复杂的闭包状态,以生成斐波那契数列。

use nclosure::prelude::*;

/// Make a function that generates the Fibonacci sequence, starting from 0
pub fn fibonacci() -> ClosureMut<(usize, usize), (), usize> {
    // Note the way this fluently creates a tuple
    closure_state().and(0).and(1)
        // And the way this fluently accesses it by mutable reference to each component 
        // with names.
        // If you keep the names of state parameters aligned with the arguments to
        // this function you can get near-native closure ergonomics feel.
        .finalise_mut(|(current, next), ()| {
            let result = *current;
            (*current, *next) = (*next, *next + *current);
            result
        })
}

let mut fibgen = fibonacci().as_fn_mut();
let vals = [fibgen(()), fibgen(()), fibgen(()), fibgen(()), fibgen(()), fibgen(())];
assert_eq!(vals, [0usize, 1, 1, 2, 3, 5]);

闭包组合

此包的一个有用方面是干净地组合计算和状态,以生成可命名的类型。本节说明了如何进行这种操作。

链式闭包(平方计数器)

这演示了以可命名的方式将闭包链在一起的一个示例。

use nclosure::prelude::*;
use core::ops::Mul;

pub fn counter() -> ClosureMut<usize, (), usize> {
    single_closure_state(0usize)
        .finalise_mut(|ctr, ()| {
            let res = *ctr;
            *ctr += 1;
            res
        }) 
} 

/// Square the output of a function 
/// Note here that the output type is actually *named*, which enables 
/// some very useful properties.  In particular, if the input computation 
/// implements `Co` or `CoMut` instead of just `CoOnce`, then the 
/// composed computation will also implement those traits (at least as 
/// long as the chained computations also implement them).
pub fn square_result<Args, IC: CoOnce<Args>>(input_computation: IC) 
    -> nclosure::Chain<(IC, fn(IC::Output) -> <IC::Output as Mul>::Output)>  
    where IC::Output: Mul + Copy {
   input_computation.and_then(|v| v * v) 
}

// Note how when the counter is `CoMut`, the resulting function is also.
let mut squared_ctr = square_result(counter()).as_fn_mut();
let vals = [squared_ctr(()), squared_ctr(()), squared_ctr(()), squared_ctr(())];
assert_eq!(vals, [0usize, 1, 4, 9])

依赖项