I've got one piece of Rust code that compiles and one that's very similar that does not.
The one that works:
pub fn do_something(_: Box<Iterator<Item = f64>>) {}
fn main() {
let iter = Box::new(vec![1.0].into_iter());
do_something(iter);
}
The one that fails:
pub fn do_something(_: Box<Box<Iterator<Item = f64>>>) {}
fn main() {
let iter = Box::new(Box::new(vec![1.0].into_iter()));
do_something(iter);
}
The difference is I have a Box<Box<..>>
instead of a Box<..>
I get the following error:
error[E0308]: mismatched types
--> src/main.rs:5:18
|
5 | do_something(iter);
| ^^^^ expected trait std::iter::Iterator, found struct `std::vec::IntoIter`
|
= note: expected type `std::boxed::Box<std::boxed::Box<std::iter::Iterator<Item=f64> + 'static>>`
found type `std::boxed::Box<std::boxed::Box<std::vec::IntoIter<{float}>>>`
I'm interpreting this error to say "IntoIter
does not have the trait Iterator
" .. but it does. What's the issue?
To be honest, I'm no expert in Rust at all, but my expectation would have been that both of the snippets you show do not compile. That is because, as you pointed out,
Iterator
is a trait and not a type and basically you wantdo_something
to receive any type which implementsIterator
. Maybe there exists a shortcut such that the compiler can transform the signature into a generic if one of the types is a trait which could be why is sometimes works, but then I'm also not familiar with the Rust language specification enough.Instead of having
do_something
take something of typeIterator
(?) make it a generic of typeT
whereT
is trait bound.Playground
Alternatively, you constrain
do_something
entirely tostd::vec::IntoIter
and only take parameters of that type.Playground
You can't coerce a
Box<Box<I>>
into aBox<Box<Iterator<Item = f64>>>
, for reasons discussed in this question, but you can coerce the innerBox
:Playground.
This works because a cast is a coercion site. By writing
as Box<Iterator<Item = f64>>
, you're hinting to the compiler that it should attempt to make the expression to the left fit that type instead of inferringBox<IntoIter<f64>>
, because once it's wrapped up in the "outer"Box
, you can't change it anymore.Alternatively (but less clearly), you could make
Box::new(...)
a coercion site by explicitly parameterizingBox
:Which effectively does the same thing.