Trying to understand compiler error - need trivial cast

While implementing a crate of mine, I encountered a strange compiler error.
I implemented a trait of the crate le-stream for an array like so:

impl<T, const SIZE: usize> ToLeBytes for [T; SIZE]
where
    T: ToLeBytes,
{
    type Iter = FlatMap<IntoIter<T, SIZE>, <T as ToLeBytes>::Iter, fn(T) -> <T as ToLeBytes>::Iter>;

    fn to_le_bytes(self) -> Self::Iter {
        self.into_iter().flat_map(<T as ToLeBytes>::to_le_bytes)
    }
}

So far, so good. But then I tried the same approach for heapless::Vec:

#[cfg(feature = "heapless")]
impl<T, const SIZE: usize> ToLeBytes for heapless::Vec<T, SIZE>
where
    T: Sized + ToLeBytes,
{
    type Iter = std::iter::Chain<
        size_prefix_iterator::SizePrefixIterator,
        FlatMap<
            <Self as IntoIterator>::IntoIter,
            <T as ToLeBytes>::Iter,
            fn(T) -> <T as ToLeBytes>::Iter,
        >,
    >;

    fn to_le_bytes(self) -> Self::Iter {
        size_prefix_iterator::SizePrefixIterator::new(self.len(), SIZE)
            .chain(self.into_iter().flat_map(<T as ToLeBytes>::to_le_bytes))
    }
}

Resulting in this error:

error[E0308]: mismatched types
   --> src/to_le_bytes/impls.rs:134:9
    |
133 |       fn to_le_bytes(self) -> Self::Iter {
    |                               ---------- expected `std::iter::Chain<SizePrefixIterator, FlatMap<heapless::vec::IntoIter<T, SIZE>, <T as ToLeBytes>::Iter, fn(T) -> <T as ToLeBytes>::Iter>>` because of return type
134 | /         size_prefix_iterator::SizePrefixIterator::new(self.len(), SIZE)
135 | |             .chain(self.into_iter().flat_map(<T as ToLeBytes>::to_le_bytes))
    | |____________________________________________________________________________^ expected fn pointer, found fn item
    |
    = note: expected struct `std::iter::Chain<_, FlatMap<_, _, fn(_) -> _>>`
               found struct `std::iter::Chain<_, FlatMap<_, _, fn(_) -> _ {<T as ToLeBytes>::to_le_bytes}>>`

For more information about this error, try `rustc --explain E0308`.
error: could not compile `le-stream` (lib) due to 1 previous error

If I just add a trivial cast, however, it compiles:

#[cfg(feature = "heapless")]
impl<T, const SIZE: usize> ToLeBytes for heapless::Vec<T, SIZE>
where
    T: Sized + ToLeBytes,
{
    type Iter = std::iter::Chain<
        size_prefix_iterator::SizePrefixIterator,
        FlatMap<
            <Self as IntoIterator>::IntoIter,
            <T as ToLeBytes>::Iter,
            fn(T) -> <T as ToLeBytes>::Iter,
        >,
    >;

    fn to_le_bytes(self) -> Self::Iter {
        #[allow(trivial_casts)]
        size_prefix_iterator::SizePrefixIterator::new(self.len(), SIZE).chain(
            self.into_iter()
                .flat_map(<T as ToLeBytes>::to_le_bytes as _),
        )
    }
}

Why is this necessary when implementing the iterator for the heapless::Vec but not for an array?

Almost surely the extra indirection involved. In the first case, this call

self.into_iter().flat_map(<T as ToLeBytes>::to_le_bytes)

was in a context that expects a FlatMap<_, _, fn(_) -> _>. A call to flat_map::<_, F> produces a FlatMap<_, _, F>, and the argument you pass in has type F. Inference can use the context to decide F = fn(_) -> _ and coerce the argument you pass in accordingly, since the argument has the same type F.

Whereas here:

// Context expects a `Chain<_, FlatMap<_, _, fn(_) -> _>>`
..::new(..).chain(
    self.into_iter().flat_map(<T as ToLeBytes>::to_le_bytes)
)

A call to .chain::<U> produces a Chain<_, U::IntoIter> when the argument you pass in has type U. Inference doesn't try to find all implementors of IntoIterator with a matching associated type (and there could be many solutions anyway), so the context has no direct effect on what is expected as the argument to chain.

Thus there is no context expecting a particular type for the nested call to flat_map and the argument doesn't get coerced, leading to the error later when the types don't line up.

1 Like

Here's a playground where I remove the IntoIterator indirection, enabling enough context for the coercion to happen automatically.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.