How to use the Fn traits (closures) in Rust function signatures?
I want to write an
i32
-returning function that accepts a closure taking zero arguments, a closure taking one argument, and a closure taking two arguments, where all closure arguments are of typei32
and every closure returnsf32
.What is that function's signature going to look like?
It looks like this:
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);
}
You can use Fn
instead of FnMut
(and remove mut
before f1
, f2
and f3
) if you want to force the caller to pass closures which don't mutate their environment, but in general, I think, you would want to use FnMut
.
This code uses unboxed closure sugar and overloaded calls. Without them, it would look like this:
#![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);
}
The sugar is used to prettify closure type syntax, and the overloaded calls feature allows to omit explicit call_*
methods.
Before Rust 1.0
Editor's note This question was asked before Rust 1.0, and this section only applies to the changes that happened between then and 1.0.
As for what's going to change in future, then it is likely that closure construction syntax would be simplified (when current closures are dropped), so the main()
bit will change from this:
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);
}
to look like this:
fn main() {
let x = closures(
|| 0.1,
|x| (2*x) as f32,
|x, y| (x + y) as f32
);
println!("{}", x);
}
The actual type of the closure (FnMut
, Fn
or FnOnce
) is going to be inferred.
There will also be other changes, like the move
keyword for closures which are returned from functions (move
affects variable capturing semantics). This is covered by this accepted RFC.
In general, unboxed closures are outlined in this RFC. It is not updated, however, with new closures sugar syntax and with other subtle changes; it may be better to follow Rust issue tracker to find out more on this. For example, a lot of issues with unboxed closures are aggregated in this bug.
Fn
, FnMut
and FnOnce
are the three trait types that were introduced with unboxed closures. The difference between these traits, besides the name of their single method, is that the self
parameter on these methods is passed differently:
Fn
:&self
(by reference, can't mutate the closure's environment)FnMut
:&mut self
(by reference, can mutate the closure's environment)FnOnce
:self
(by value, consumes the closure, so the closure cannot be called more than once)
These traits have one type parameter, Args
, which is a tuple type that represents the closure's parameters (or ()
if the closure takes no parameters). FnOnce
has the associated type Result
, which is the closure's return type. Fn
is a subtrait of FnMut
, and FnMut
is a subtrait of FnOnce
, which means that Fn
and FnMut
"inherit" Result
from FnOnce
. Unboxed closures automatically implement the applicable traits.
Sugared form
Closure taking zero arguments
fn foo<F: Fn() -> f32>(closure: F) -> i32 {
0
}
Closure taking one argument
fn foo<F: Fn(i32) -> f32>(closure: F) -> i32 {
0
}
Closure taking two arguments
fn foo<F: Fn(i32, i32) -> f32>(closure: F) -> i32 {
0
}
Using a where
clause
Each of these can also use the where
syntax:
fn foo<F>(closure: F) -> i32
where
F: Fn() -> f32,
{
0
}
See also:
- What's the difference between `<T: Trait>` and `where T: Trait`?
Using the impl trait
syntax:
fn foo_impl(closure: impl Fn() -> f32) -> i32 {
0
}
See also:
- What are the differences between an impl trait argument and generic function parameter?
- What does `impl` mean when used as the argument type or return type of a function?
- What makes `impl Trait` as an argument "universal" and as a return value "existential"?
Desugared versions
This format is unstable, and each of these examples requires using the feature gate #![feature(unboxed_closures)]
. You can also use the where
or impl trait
syntax.
See also:
- How do I implement the Fn trait for one struct for different types of arguments?
Closure taking zero arguments
fn foo<F: Fn<(), Output = f32>>(closure: F) -> i32 {
0
}
Closure taking one argument
fn foo<F: Fn<(i32,), Output = f32>>(closure: F) -> i32 {
0
}
Closure taking two arguments
fn foo<F: Fn<(i32, i32), Output = f32>>(closure: F) -> i32 {
0
}
Old "boxed" closures
"Boxed" closures existed at the time this question was asked, but they were removed before Rust 1.0.
This metabug tracked the development of unboxed closures.