Home > Software engineering >  How to type None if it expects a function type as type argument?
How to type None if it expects a function type as type argument?

Time:01-14

Suppose I have a function that takes a callback

fn foo<T>(callback: impl FnOnce(T) -> T, value: T) -> T {
    callback(value)
}

Suppose I now want to make this callback optional. The, in my view, most obvious way of doing that is to use Option:

fn foo<T>(callback: Option<impl FnOnce(T) -> T>, value: T) -> T {
    if let Some(callback) = callback {
        callback(value)
    } else {
        value
    }
}

However, when doing this, I run into a problem at the use site. For instance, the following code does not compile:

fn bar() -> u8 {
    let value: u8 = b'.';
    let value = foo(None, value);
    value
}

I get the following error:

error[E0282]: type annotations needed
  --> XX:XX:XX
   |
XX |     let value = foo(None, value);
   |                     ^^^^ cannot infer type of the type parameter `T` declared on the enum `Option`
   |
help: consider specifying the generic argument
   |
XX |     let value = foo(None::<T>, value);
   |                              

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

However, it seems impossible to provide a type for this, as impl … syntax does not work as a type argument here, and you’d need a concrete type from a closure anyways.

Is it possible to annotate the type for a missing closure like this?

CodePudding user response:

You have to provide it a type that implements FnOnce(u8) -> u8 since that is what foo requires, the simplest one I can think of is a function pointer:

fn bar() -> u8 {
    let value: u8 = b'.';
    let value = foo(None::<fn(_) -> _>, value);
    value
}

CodePudding user response:

There are a few ways. You'll need to pick a type that implements FnOnce(T) -> T. For the examples, I'll use fn(T) -> T, but it doesn't make much difference:

  • use a turbofish:
foo(None::<fn(u8) -> u8>, 123);
  • use a let binding with a type ascription
let option_fn: Option<fn(u8) -> u8> = None;
foo(option_fn, 123);
  • make the generics named, and use the turbofish on foo:
fn foo<T, F: FnOnce(T) -> T>(f: F, t: T) -> T {
  ...
}

foo::<u8, fn(u8) -> u8>(None, 123);
  • Related