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 type i32 and every closure returns f32.

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.

Tags:

Rust