【问题标题】:How to use the Fn traits (closures) in Rust function signatures?如何在 Rust 函数签名中使用 Fn 特征(闭包)?
【发布时间】:2020-02-06 09:50:54
【问题描述】:

我想写一个i32-returning 函数,它接受一个带零参数的闭包、一个带一个参数的闭包和一个带两个参数的闭包,其中所有闭包参数的类型都是i32,每个闭包都返回@ 987654323@.

该函数的签名会是什么样子?

我想通过 FnFnMut 特征接受这些。签名是什么样子的?是否需要使用 crate 中的功能?如果是,是哪些,为什么?

如果知道:加糖看起来像什么?脱糖了吗?

如果知道:未来可能会发生什么变化?

【问题讨论】:

    标签: rust


    【解决方案1】:

    我想写一个接受闭包的i32-returning 函数 接受零个参数,一个闭包接受一个参数,和一个闭包 采用两个参数,其中所有闭包参数的类型均为 i32 和 每个闭包都返回f32

    该函数的签名会是什么样子?

    看起来像这样:

    fn closures<F1, F2, F3>(mut f1: F1, mut f2: F2, mut f3: F3) -> i32
    where
        F1: FnMut() -> f32,
        F2: FnMut(i32) -> f32,
        F3: FnMut(i32, i32) -> f32,
    {
        (f1() + f2(10) + f3(20, 30)) as i32
    }
    
    fn main() {
        let x = closures(|| 0.1, |x| (2 * x) as f32, |x, y| (x + y) as f32);
        println!("{}", x);
    }
    

    如果你想强制调用者传递不改变环境的闭包,你可以使用Fn而不是FnMut(并在f1f2f3之前删除mut) ,但总的来说,我认为你会想要使用FnMut

    此代码使用未装箱的闭包糖和重载调用。没有它们,它看起来像这样:

    #![feature(unboxed_closures, fn_traits)]
    
    fn closures<F1, F2, F3>(mut f1: F1, mut f2: F2, mut f3: F3) -> i32
    where
        F1: FnMut<(), Output = f32>,
        F2: FnMut<(i32,), Output = f32>,
        F3: FnMut<(i32, i32), Output = f32>,
    {
        (f1.call_mut(()) + f2.call_mut((10,)) + f3.call_mut((20, 30))) as i32
    }
    
    fn main() {
        let x = closures(|| 0.1, |x| (2 * x) as f32, |x, y| (x + y) as f32);
        println!("{}", x);
    }
    

    糖用于美化闭包类型语法,重载调用功能允许省略显式call_* 方法。

    Rust 1.0 之前

    编者注这个问题是在 Rust 1.0 之前提出的,本节仅适用于从那时到 1.0 之间发生的变化。

    至于未来会发生什么变化,那么闭包构造语法很可能会被简化(当当前的闭包被丢弃时),所以main()位将从这里改变:

    fn main() {
        let x = closures(
            |&mut:| 0.1,
            |&mut: x: int| (2*x) as f32,
            |&mut: x: int, y: int| (x + y) as f32
        );
        println!("{}", x);
    }
    

    看起来像这样:

    fn main() {
        let x = closures(
            || 0.1,
            |x| (2*x) as f32,
            |x, y| (x + y) as f32
        );
        println!("{}", x);
    }
    

    将推断闭包的实际类型(FnMutFnFnOnce)。

    还会有其他更改,例如用于从函数返回的闭包的 move 关键字(move 影响变量捕获语义)。 this 接受的 RFC 涵盖了这一点。

    一般来说,未装箱的闭包在this RFC 中进行了概述。但是,它没有更新,包括新的闭包糖语法和其他细微的变化;最好关注 Rust issue tracker 以了解更多信息。例如,很多未装箱闭包的问题都汇总在this 错误中。

    【讨论】:

    • 嗯。我正在查看 RFC,试图找到有关 Fn 特征的信息,我想我错过了它,因为我正在寻找“特征”而不是“闭包”。谢谢!
    • 是的,这些东西被称为未装箱的闭包,如果你寻找这个术语,你会发现很多。
    【解决方案2】:

    FnFnMutFnOnce 是随未装箱闭包引入的三种特征类型。这些特征之间的区别,除了它们单个方法的名称外,还在于这些方法上的self 参数的传递方式不同:

    • Fn: &amp;self(通过引用,不能改变闭包的环境)
    • FnMut: &amp;mut self(通过引用,可以改变闭包的环境)
    • FnOnce:self(按值,消耗闭包,因此不能多次调用闭包)

    这些特征有一个类型参数Args,它是一个表示闭包参数的元组类型(如果闭包没有参数,则为())。 FnOnce 具有关联类型 Result,这是闭包的返回类型。 FnFnMut 的子特征,FnMutFnOnce 的子特征,这意味着FnFnMutFnOnce“继承”Result。未装箱的闭包会自动实现适用的特征。

    加糖形式

    采用零参数的闭包

    fn foo<F: Fn() -> f32>(closure: F) -> i32 {
        0
    }
    

    闭包接受一个参数

    fn foo<F: Fn(i32) -> f32>(closure: F) -> i32 {
        0
    }
    

    带有两个参数的闭包

    fn foo<F: Fn(i32, i32) -> f32>(closure: F) -> i32 {
        0
    }
    

    使用where 子句

    每个都可以使用where 语法:

    fn foo<F>(closure: F) -> i32
    where
        F: Fn() -> f32,
    {
        0
    }
    

    另见:

    使用impl trait 语法:

    fn foo_impl(closure: impl Fn() -> f32) -> i32 {
        0
    }
    

    另见:

    脱糖版本

    此格式不稳定,每个示例都需要使用功能门#![feature(unboxed_closures)]。您也可以使用whereimpl trait 语法。

    另见:

    采用零参数的闭包

    fn foo<F: Fn<(), Output = f32>>(closure: F) -> i32 {
        0
    }
    

    闭包接受一个参数

    fn foo<F: Fn<(i32,), Output = f32>>(closure: F) -> i32 {
        0
    }
    

    带有两个参数的闭包

    fn foo<F: Fn<(i32, i32), Output = f32>>(closure: F) -> i32 {
        0
    }
    

    旧的“盒装”瓶盖

    在提出这个问题时存在“盒装”闭包,但在 Rust 1.0 之前它们是 removed

    This metabug 跟踪未装箱闭包的开发。

    【讨论】:

      猜你喜欢
      • 2020-12-30
      • 2016-10-05
      • 2022-01-17
      • 1970-01-01
      • 2022-12-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多