I need a function that gets an Option
of an generic type T
that implements the trait std::iter::IntoIterator
.
A naive implementation could look like the following (yes, the unwrap would panic on None
):
fn main() {
let v = vec![1i32, 2, 3];
print_iter(Some(v));
print_iter(None);
}
fn print_iter<T: IntoIterator<Item = i32>>(v: Option<T>) {
for e in v.unwrap() {
println!("{}", e);
}
}
Test on playground.
This works as expected for Some(...)
, but fails for None
with:
error[E0282]: type annotations needed
--> src/main.rs:4:5
|
4 | print_iter(None);
| ^^^^^^^^^^ cannot infer type for `T`
Obviously the type of T
is unknown in those cases. One could use print_iter::<Vec<i32>>(None);
but this does not feel really idiomatic, because this gives some arbitrary type that isn't based on anything...
Is there any way to hint to the compiler that I don't care for None
or use some kind of default?
Is there any way to hint to the compiler that I don't care for
None
or use some kind of default?
You can implement your own non-generic value to serve as the default. For starters, let's assume print_iter
didn't accept Option<T>
, but an enum of its own:
enum PrintArg<T> {
Ignore,
Use(T),
}
fn print_iter<T: IntoIterator<Item = i32>>(v: PrintArg<T>) {
if let PrintArg::Use(v) = v {
for e in v {
println!("{}", e);
}
}
}
This doesn't solve the problem yet, because if you pass PrintArg::Ignore
to print_iter()
, you are back at square one - the compiler is unable to to infer the T
. But with your own type, you can easily change print_iter
to accept anything that can be converted into PrintArg
:
fn print_iter<T, V>(v: T)
where
T: Into<PrintArg<V>>,
V: IntoIterator<Item = i32>,
{
if let PrintArg::Use(v) = v.into() {
for e in v {
println!("{}", e);
}
}
}
With this modification, you can create a dummy non-generic Ignore
value and use the From
trait to define its conversion to a PrintArg::Ignore<T>
with T
of your choice - for example:
struct Ignore;
impl From<Ignore> for PrintArg<Vec<i32>> {
fn from(_v: Ignore) -> Self {
PrintArg::Ignore
}
}
As Ignore
is non-generic, its use doesn't require (or accept) a <T>
. While we did have to invent a type for PrintArg<T>
in the From
trait implementation, we never construct it, so it's irrelevant which one we choose, as long as it satisfies the IntoIterator
bound.
Of course, you'll still want to be able to invoke print_iter()
with Some(...)
, so you'll also define a conversion of Option<T>
to PrintArg<T>
:
impl<T> From<Option<T>> for PrintArg<T> {
fn from(v: Option<T>) -> Self {
match v {
Some(v) => PrintArg::Use(v),
None => PrintArg::Ignore,
}
}
}
With these in place, your API is clean, allowing main()
to look like this (playground):
fn main() {
let v = vec![1i32, 2, 3];
print_iter(Some(v));
print_iter(Ignore);
}
There's no problem with the value None
.
There's just a problem when your variable type can't be inferred which doesn't usually occur. The only case where there's a problem is when you directly pass None
, and have no typed variable.
You can specify the type using the turbo fish:
print_iter::<Vec<i32>>(None);
But you don't usually need to; your normal cases would rather be like this:
let a: Option<Vec<i32>> = None;
print_iter(a);
or
print_iter(my_options.a);
and both constructs are without problem.
Now (following question edit), if you really want to be able to pass None
without precising the type, for example as a literal flag, then you can define a macro:
macro_rules! pi {
(None) => {
// here we handle the call as a nop but
// other behaviors are possible, like
// calling the function with a type
// specified with a turbofish
};
($a:expr) => {
print_iter($a)
};
}
fn main() {
let v = vec![1i32, 2, 3];
pi!(Some(v));
pi!(None);
}
fn print_iter<T: IntoIterator<Item = i32>>(v: Option<T>) {
for e in v.unwrap() {
println!("{}", e);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With