那么这个函数的签名将是什么样子的?
我想通过
Fn
和FnMut
traits来接受它们。这个签名是什么样子的?需要使用crate中的特性吗?如果需要,是哪些特性以及为什么?如果已知:糖语法是什么样子的?解糖之后呢?
如果已知:未来可能会有哪些变化?
Fn
和FnMut
traits来接受它们。这个签名是什么样子的?需要使用crate中的特性吗?如果需要,是哪些特性以及为什么?我想编写一个返回的函数,该函数接受一个不带参数的闭包、一个带一个参数的闭包和一个带两个参数的闭包,其中所有闭包参数都是类型,每个闭包都返回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
(并在 f1
、f2
和 f3
前删除 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之前提出,本部分仅适用于那时与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);
}
闭包的实际类型(FnMut
、Fn
或 FnOnce
)将被推断出来。
还会有其他变化,例如用于从函数返回的闭包的move
关键字(move
会影响变量捕获语义)。这在此已接受的RFC中进行了讨论。
通常,在此 RFC 中概述了未装箱的闭包。然而,它没有更新新的闭包语法糖和其他微妙的更改,可能更好的方式是跟随Rust问题追踪器以获取更多信息。例如,许多未装箱的闭包问题都聚集在此bug中。
Fn
、FnMut
和 FnOnce
是使用非盒式闭包引入的三种特质类型。这些特质之间除了单个方法名称不同以外,最大的区别是它们的方法中的 self
参数传递方式不同:
Fn
: &self
(通过引用传递,无法更改闭包的环境)FnMut
: &mut self
(通过引用传递,可以更改闭包的环境)FnOnce
: self
(通过值传递,消耗闭包,因此无法多次调用闭包)这些特质都有一个类型参数 Args
,它是表示闭包参数的元组类型(如果闭包不带参数,则为 ()
)。FnOnce
具有关联类型 Result
,它是闭包的返回类型。 Fn
是 FnMut
的子特质,而 FnMut
是 FnOnce
的子特质,这意味着 Fn
和 FnMut
从 FnOnce
中“继承”了 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)]
。您也可以使用 where
或 impl 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 发布之前被移除了。
这个 metabug跟踪了非盒装闭包的开发。