How does this three-line-change fix my Iterator compiler error?

I have some code that takes an iterator defined by a crate and consumes it. I've narrowed the bug in my project down to something to do with the type signature, with a very constrained example of the fix here.

What does this compiler error mean, and why is it fixed by changing the type signature of the outer function? It doesn't change the fact that the Iterator trait has always been implemented for the given type, so I'm confused why this is a 'trait not implemented' error instead of a 'expected type Foo, got type Bar' error.

error[E0599]: no method named `next` found for type `rusqlite::row::MappedRows<'_, F>` in the current scope
 --> src/main.rs:5:7
  |
5 |     m.next().unwrap()
  |       ^^^^
  |
  = note: the method `next` exists but the following trait bounds were not satisfied:
          `rusqlite::row::MappedRows<'_, F> : std::iter::Iterator`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0599`.
error: Could not compile `iterators`.

To learn more, run the command again with --verbose.

Type in question is defined here

So, as you can see, the only way for MappedRows<'_, F> to be an iterator is for F to be FnMut(&Row<'_>) -> their::Result<T>; it seems to be related to their using the and_then combinator on row_result.

If such struct is only supposed to be instanciated with closures of that signature (which seems to be the case), they could add the bound to the struct definition, and in that case you would have gotten a more readable type error:

44 | / pub
45 | | fn fetch_one_row<T, E, F>(
46 | |     mut m: rusqlite::MappedRows<'_, F>,
47 | | ) -> Result<T, E>
...  |
51 | |     m.next().unwrap()
52 | | }
   | |_^ expected type parameter, found struct `rusqlite::MyErr`
   |
   = note: expected type `std::result::Result<T, E>`
              found type `std::result::Result<_, rusqlite::MyErr>`

You could file an issue for them to add such correction, although it is a non-backwards-compatible change that would thus require a major version bump.

1 Like

So in interpreting this compiler error note:

  = note: the method `next` exists but the following trait bounds were not satisfied:
          `rusqlite::row::MappedRows<'_, F> : std::iter::Iterator`

rustc is telling me that the trait is implemented, but only under the specific context of satisfying the kinds of types specified in the implementation signature:

impl<T, F> Iterator for MappedRows<'_, F>

specifically, I need to make sure that I'm also specifying the same constraints, like returning the same types (rusqlite::Result<T>) and that the closure has the same signature.

That's really interesting that there are different ways to accomplish the same API, but by moving the bounds they could give users a clearer error message!

Thanks, @Yandros!

There are some downsides to doing this, notably it makes the type less flexible. But in this case, that should be a worthwhile trafe off.

1 Like