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
每月下载量6,376次
用于linotype
14KB
94 行
::高阶-阶-闭包
允许在闭包上启用函数生命周期省略和显式for<'a>
注释。
动机/理由
参见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, ...
>
主要是
-
每个生命周期最多有一个超生命周期限制,
-
类型上的超限制必须正好是以下形式
- 可选的
?Sized
, - 后面跟着一个可选的生命周期限制,
- 后面跟着一个可选的特质的限制。
- 就这些。如果您需要更多的灵活性,请使用
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
界限,从而使其作为一个"漏斗"工作,只允许具有正确签名的闭包通过)。