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);
}