Reputation: 2290
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?
Upvotes: 1
Views: 87
Reputation: 168998
As an alternative to the existing answers, supplying an identity function (one that returns its argument) would be the more appropriate functional style here, and has the advantage that it makes the whole problem go away. It is also likely to perform better as there's no branch that can be incorrectly predicted.
fn identity<T>(v: T) -> T { v }
fn foo<T>(callback: impl FnOnce(T) -> T, value: T) -> T {
callback(value)
}
fn bar() -> u8 {
let value: u8 = b'.';
let value = foo(identity, value);
value
}
In other words, using Option
can actually be wasteful when "no function" will cause the same observable behavior as "the identity function."
(Of course, |v| v
is shorter than identity
, so which you use is a matter of taste.)
This doesn't solve the general problem of how you specify None
in the context of an optional function when the identity function shouldn't cause the same behavior as no function at all. The other answers address how to handle that situation. This answer is intended to provide a better solution when both cases do have the same behavior.
Upvotes: 1
Reputation: 10136
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:
foo(None::<fn(u8) -> u8>, 123);
let option_fn: Option<fn(u8) -> u8> = None;
foo(option_fn, 123);
foo
:fn foo<T, F: FnOnce(T) -> T>(f: F, t: T) -> T {
...
}
foo::<u8, fn(u8) -> u8>(None, 123);
Upvotes: 1
Reputation: 27283
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
}
Upvotes: 3