#高阶 #闭包 #生命周期 #签名 #hrtb

higher-order-closure

允许在闭包上启用函数生命周期省略和显式for<'a>注释

5个版本

0.0.5 2022年1月30日
0.0.4 2022年1月30日
0.0.3 2022年1月30日
0.0.2 2022年1月30日
0.0.1 2022年1月30日

编程语言中排名第273

Download history 1915/week @ 2024-03-14 1665/week @ 2024-03-21 1535/week @ 2024-03-28 1806/week @ 2024-04-04 1750/week @ 2024-04-11 1828/week @ 2024-04-18 1445/week @ 2024-04-25 1405/week @ 2024-05-02 1521/week @ 2024-05-09 1658/week @ 2024-05-16 1049/week @ 2024-05-23 1788/week @ 2024-05-30 1495/week @ 2024-06-06 1616/week @ 2024-06-13 1790/week @ 2024-06-20 1069/week @ 2024-06-27

每月下载量6,376
用于linotype

使用Zlib OR MIT OR Apache-2.0许可证

14KB
94

::高阶--闭包

允许在闭包上启用函数生命周期省略和显式for<'a>注释。

Repository Latest version Documentation MSRV unsafe forbidden License CI

动机/理由

参见RFC #3216:这个crate是该RFC中提出的想法的Proof-of-Concept。

^[1]: 除了允许省略的生命周期具有意义(选择高阶)之外,RFC不能这样做,以保持对未来友好。

点击展开

以下示例无法编译

let f = |x| {
    let _: &i32 = x;
    x
};
{
    let scoped = 42;
    f(&scoped);
} // <- scoped dropped here.
f(&42);
error[E0597]: `scoped` does not live long enough
  --> src/lib.rs:10:7
   |
10 |     f(&scoped);
   |       ^^^^^^^ borrowed value does not live long enough
11 | } // <- scoped dropped here.
   | - `scoped` dropped here while still borrowed
12 | f(&42);
   | - borrow later used here

For more information about this error, try `rustc --explain E0597`.

确实,该示例中f的签名是

impl Fn(&'inferred i32) -> &'inferred i32

其中'inferred代表某些尚未知的(需要推断的)但已确定的生命周期。

然后,

{
    let scoped = 42;
    f(&scoped); // `'inferred` must "fit" into this borrow…
} // <- and thus can't span beyond this point.
f(&42) // And yet `'inferred` is used here as well => Error!

解决方案是显式注释闭包签名中涉及的类型,更重要的是,该签名中涉及的**生命周期“空位”/占位符/参数**。

           // Rust sees this "hole" early enough in its compiler pass
           //                       to figure out that the closure signature
           // vv                    needs to be higher-order, **input-wise**
let f = |_x: &'_ i32| {
};
{
    let scoped = 42;
    f(&scoped);
}
f(&42);

这使得闭包签名的一侧实际上成为了高阶。不再是

impl Fn(&'inferred_and_thus_fixed i32)...

对于某个外部推断的(因此,固定的)生命周期'inferred_and_thus_fixed,我们现在有

impl for<'any> Fn(&'any i32)...

这可以工作,但**关于返回借用的问题是什么**?(同时保持高阶)

确实,以下示例无法编译

let f = |x: &'_ i32| -> &'_ i32 {
    x // <- Error, does not live long enough.
};
error: lifetime may not live long enough
 --> src/lib.rs:5:5
  |
4 | let f = |x: &'_ i32| -> &'_ i32 {
  |             -           - let's call the lifetime of this reference `'2`
  |             |
  |             let's call the lifetime of this reference `'1`
5 |     x // <- Error, does not live long enough.
  |     ^ returning this value requires that `'1` must outlive `'2`

原因是“显式生命周期‘空位’/占位符成为闭包签名中高阶生命周期参数”的机制仅适用于签名的一侧。

返回侧仍然使用推断的生命周期

let f = /* for<'any> */ |x: &'any i32| -> &'inferred i32 {
    x // <- Error, does not live long enough (when `'any < 'inferred`)
};

我们希望那里的f具有从函数签名的生命周期省略规则获得的签名,即

因此使用这个crate的原因。

示例

此crate提供了一个higher_order_closure!宏,可以通过它正确地注释闭包签名,使其成为高阶的,具有普遍量化("forall"量化,Rust中的for<>)的生命周期

#[macro_use]
extern crate higher_order_closure;

fn main ()
{
    let f = higher_order_closure! {
        for<'any> |x: &'any i32| -> &'any i32 {
            x
        }
    };
    {
        let local = 42;
        f(&local);
    }
    f(&42);
}

函数签名的生命周期省略规则适用,这意味着之前的签名甚至可以简化为

#[macro_use]
extern crate higher_order_closure;

fn main ()
{
    let f =
        higher_order_closure! {
            for<> |x: &'_ i32| -> &'_ i32 {
                x
            }
        }
    ;
}

或者甚至

#[macro_use]
extern crate higher_order_closure;

fn main ()
{
    let f =
        higher_order_closure! {
            |x: &'_ i32| -> &'_ i32 {
                x
            }
        }
    ;
}
  • 由于这些函数签名语义中的生命周期省略,强烈建议在使用此宏时至少将elided_lifetimes_in_paths设置为warn

    //! At the root of the `src/{lib,main}.rs`.
    #![warn(elided_lifetimes_in_paths)]
    

额外功能

宏缩写

主要宏以hrtb!缩写形式重新导出,以允许内联闭包并减少向右偏移

#[macro_use]
extern crate higher_order_closure;

fn main ()
{
    let f = {
        let y = 42;
        hrtb!(for<'any> move |x: &'any i32| -> &'any i32 {
            println!("{y}");
            x
        })
    };
}

外部泛型参数

由于宏的内部工作方式[^2],在作用域内的泛型参数默认情况下不会在闭包签名中可用(类似于const和嵌套函数或类型定义)。

为了使它们可用,higher_order_signature!接受一个可选的初始参数#![with<simple generics…>](或者如果泛型参数的"简单形状"限制过于严格,甚至可以接受#![with<simple generics…> where clauses])。

"简单形状"泛型宏限制

#![with<…>]内的泛型参数必须具有以下形式

<
    'a, 'b : 'a, ...
    T, U : ?Sized + 'a + ::core::fmt::Debug, V, ...
>

主要是

  • 每个生命周期最多有一个超生命周期限制,

  • 类型上的超限制必须正好是以下形式

    1. 可选的?Sized
    2. 后面跟着一个可选的生命周期限制,
    3. 后面跟着一个可选的特质的限制。
    4. 就这些。如果您需要更多的灵活性,请使用where子句。

然而,在实践中,界限很少需要,因为此类泛型仅用于闭包的签名,而不是其主体/实现。

#[macro_use]
extern crate higher_order_closure;

use ::core::fmt::Display;

fn example<T : Display> (iter: impl IntoIterator<Item = (bool, T)>)
{
    let mb_display = higher_order_closure! {
        #![with<T>]
        |hide: bool, elem: &'_ T| -> &'_ dyn Display {
            if hide {
                &"<hidden>"
            } else {
                elem
            }
        }
    };
    for (hide, elem) in iter {
        println!("{}", mb_display(hide, &elem));
    }
}

fn main ()
{
    example([(false, 42), (true, 27), (false, 0)]);
}

混合高阶生命周期参数和推断参数

由于在higher_order_closure!内部,'_具有函数签名的生命周期省略语义,这意味着默认情况下,所有出现在此类闭包签名中的生命周期参数必然是高阶的。

在某些更复杂或罕见的情况下,这可能是不可取的。

在这种情况下,一种很好的解决方法是人为地引入泛型生命周期参数,如上所述,并在需要推断生命周期的位置使用这些新引入的命名生命周期参数。

#[macro_use]
extern crate higher_order_closure;

fn main ()
{
    let y = 42;
    let f = higher_order_closure! {
        #![with<'inferred>]
        for<'any> |x: &'any i32| -> (&'any i32, &'inferred i32) {
            (x, &y)
        }
    };
    let (&a, &b) = f(&27);
    assert_eq!(a + b, 42 + 27);
}

[^2]:它生成一个具有所需高阶签名的"闭包身份"辅助函数,其中嵌入为参数上的Fn界限,从而使其作为一个"漏斗"工作,只允许具有正确签名的闭包通过)。

无运行时依赖

功能