3

In the following example, MyTrait extends IntoIterator but the compiler doesn't recognize it when used in a loop.

pub trait MyTrait: IntoIterator<Item = i32> {
    fn foo(&self);
}

pub fn run<M: MyTrait>(my: &M) {
    for a in my {
        println!("{}", a);
    }
}

I get the error:

error[E0277]: `&M` is not an iterator
 --> src/lib.rs:6:14
  |
6 |     for a in my {
  |              ^^ `&M` is not an iterator
  |
  = help: the trait `Iterator` is not implemented for `&M`
  = note: required because of the requirements on the impl of `IntoIterator` for `&M`
  = note: required by `into_iter`
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Guy Korland
  • 9,139
  • 14
  • 59
  • 106
  • 3
    Read the error message carefully: `O` is iterable, but `&O` isn't. – trent Apr 02 '21 at 12:24
  • I thought this is the issue, but it's not, I replaced &V with &Vec and it works – Guy Korland Apr 02 '21 at 12:39
  • 2
    That's because `&Vec` implements `IntoIterator`... – trent Apr 02 '21 at 12:44
  • 3
    And it's not just a quirk of syntax: `IntoIterator::into_iter()` takes `self`, i.e. needs to consume the value it is called on. If you have just a reference to an object, the fact that it implements `IntoIterator` is useless because you cannot use a reference to consume the object. `&Vec` provides a separate implementation of `IntoIterator` that trivially calls `self.iter()` and returns an iterator that produces references to vector elements. – user4815162342 Apr 02 '21 at 12:57
  • So I'm not sure how do I workaround it, do I need to implement `IntoIterator` for `&OtherTrait`? – Guy Korland Apr 02 '21 at 13:04
  • @Shepmaster thanks these are great references but I still don't get how implement `IntoIterator` for `&OtherTrait` – Guy Korland Apr 02 '21 at 14:13
  • `impl<'a> IntoIterator for &'a OtherTrait` – Tavian Barnes Apr 02 '21 at 14:16
  • Look at the section [after the text "you'd need to also implement an iterator that doesn't consume it"](https://stackoverflow.com/a/30220832/155423) – Shepmaster Apr 02 '21 at 14:20
  • It feels like going down the rabbit hole, new errors are popping... – Guy Korland Apr 02 '21 at 14:24
  • @TavianBarnes did you manage to compile it? – Guy Korland Apr 02 '21 at 14:34
  • @Shepmaster you were right associated was a red herring, I simplified the example. – Guy Korland Apr 02 '21 at 14:56

1 Answers1

2

Only M implements IntoIterator, but you're trying to iterate over a &M, which doesn't have to.

It's not clear what you hope to achieve with run, but removing the reference might be a start:

pub fn run<M: MyTrait>(my: M) {
    for a in my {
        println!("{}", a);
    }
}

Note that M itself may be (or contain) a reference, so writing it in this way doesn't mean you can't use it with borrowed data. Here's one way to use run to iterate over a &Vec (playground):

impl<I> MyTrait for I
where
    I: IntoIterator<Item = i32>,
{
    fn foo(&self) {}
}

fn main() {
    let v = vec![10, 12, 20];
    run(v.iter().copied());
}

This uses .copied() to turn an Iterator<Item = &i32> to an Iterator<Item = i32>.

Related questions

trent
  • 25,033
  • 7
  • 51
  • 90
  • but @trentcl that is the all issue, even if I wrap `&OtherTrait` I still need somehow to iterate over `&OtherTrait` – Guy Korland Apr 02 '21 at 14:37
  • @GuyKorland The question doesn't contain enough context to understand how you're trying to use this. Removing `&'a` from `MyEnum` [is all you need to make your example compile](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=8dff5851e0c2a2c2c520a7beb3119334). I can't know what other changes you might need to make the rest of your code compile, because I don't know what the rest of your code looks like. – trent Apr 02 '21 at 14:45
  • all I'm trying to do is pass a borrowed trait and iterate over it. Simplified the example – Guy Korland Apr 02 '21 at 14:57
  • @GuyKorland The solution to the simplified example is the same: [just remove the `&`](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=9328526405cb376fde182978a5a000d6). You **cannot** use `IntoIterator` by reference. But you *can* still use references (and structs that contain references) which themselves implement `IntoIterator`: [for example, `Copied>`](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=e39a0d540d959af13a15b025515d55ad). – trent Apr 02 '21 at 15:34
  • I should perhaps add that in principle you *could* fix this with a higher-ranked trait bound; however, since [where clauses are only elaborated for supertraits](https://github.com/rust-lang/rust/issues/20671), it [doesn't work today](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=68a4353def36553e206eba7ec91bdd47), and even if it did it still wouldn't work with `M = Vec` because `&Vec` doesn't implement `IntoIterator` (it implements `IntoIterator` -- note the `&`). – trent Apr 02 '21 at 15:55
  • `impl<'a, T, A: Allocator> IntoIterator for &'a Vec {` This is the definition it's not &T but to T. The item is `type Item = &'a T;` but that is expected cause that is result of the iterator – Guy Korland Apr 03 '21 at 12:16
  • @GuyKorland Yes, `&Vec` is a reference and implements `IntoIterator`. What's your point? – trent Apr 03 '21 at 13:17
  • That I still don't get how to implement `impl IntoIterator for &MyTrait` – Guy Korland Apr 03 '21 at 13:28
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/230692/discussion-between-trentcl-and-guy-korland). – trent Apr 03 '21 at 13:39