RPIT, confused by compiler error: "all type parameters are required to be mentioned in the precise captures list"

Hi everyone,

I am trying to write a method that returns an iterator, and I am hitting a compiler error that I don’t fully understand.

Here is a simplified version of the code:

use std::iter::once;

struct MyStruct;

impl MyStruct {
    pub fn method<'s, 'a>(
            &'s self,
            a: impl Iterator<Item = &'a u128>,
        ) -> impl Iterator<Item = u128> + use<> {
           once(0)
    }
}

The compiler complains with this note:

note: currently, all type parameters are required to be mentioned in the precise captures list

But I don’t want to capture anything, that’s the whole point for using use<> in this case.

Rust playground link.

You need to deal with your APIT in your RPIT!

a: impl Iterator<Item = &'a u128>, introduces a hidden type parameter. You need to make that type parameter explicit, and acknowledge the dependency on it (because, as the error says, you're not allowed not to).

    pub fn method<'s, 'a, I: Iterator<Item = &'a u128>>(
        &'s self,
        a: I,
    ) -> impl Iterator<Item = u128> + use<I> {

As a side note, it isn’t generally a good idea to work with references to primitives if you don’t have to, because they can be inefficient (requiring memory access instead of passing the data directly) and constraining (because there has to be something for the references to borrow). You should consider accepting u128 directly and simplifying your signature:

    pub fn method<I: Iterator<Item = u128>>(
        &self,
        a: I,
    ) -> impl Iterator<Item = u128> + use<I> {

If someone has an iterator of &u128 to pass to your method, they can simply call .copied() on the iterator.

There may be even better ways to write this, but in order to suggest any of them I’d have to see what your iterator is actually doing (how the returned iterator relates to the input iterator a).

2 Likes

It’s an unimplemented feature. (see #130043).

There’s a way to workaround this on nightly compiler, but I don’t think you can do so on stable.

2 Likes

Yes, acknowledging a dependency on APIT would make sense if RPIT actually needed to capture its lifetime. But in this case, I don’t need to capture the lifetime of that iterator. The returned iterator is not tied to the input iterator in any way, shape, or form (for the sake of argument, you can assume the input iterator is first collected into a collection). Please let me know if I’m missing something here.

Also, a primitive type is used here for simplicity.

The matter at issue is capturing your function’s type parameter, not any of your lifetime parameters. Not capturing type parameters is simply not supported by the compiler — it can’t yet perform the necessary reasoning to verify the lack of relationship you claim.

This is a limitation of the compiler’s current abilities, not a necessary property of the language.

1 Like

I am confused about the meaning of capturing just a type parameter. What does that even mean? Could you refer me to some resources where I can read more about this?

It means that

  • The return type is different when that type parameter is different. The return type of my_struct.method([&1]) is different from the return type of my_struct.method(some_vec.iter()).
  • The return type captures all lifetimes appearing in that type parameter (which might be more than just 'a — it’s up to what the caller provides).

These are both unavoidable in the case where you are returning something like an iterator that wraps the provided iterator I. And as previously discussed, the compiler doesn’t yet support not capturing in the cases where it is avoidable (but it should, someday).


Also, if you need to avoid the capture, and as you say, "assume the input iterator is first collected into a collection", then you can use an intermediate named type to help out:

impl MyStruct {
    pub fn method<'a>(
            &self,
            a: impl Iterator<Item = &'a u128>,
        ) -> IntermediateThing {
           IntermediateThing { ... }
    }
}

struct IntermediateThing { ... }

impl IntermediateThing {
    pub fn iterate(self)  -> impl Iterator<Item = u128> { ... }
}

The concrete, parameterless type IntermediateThing allows you to make the input type parameter and the return type "obviously" disconnected.

4 Likes

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.