2

A struct has a set of items I'd like to allow clients to iterate over. Here's an attempt:

use std::collections::HashSet;
use std::hash::Hash;

pub struct Foo<T: Eq + Hash> {
    items: HashSet<T>,
}

impl<'a, T: 'a + Eq + Hash> Foo<T> {
    pub fn new() -> Self {
        Foo {
            items: HashSet::new(),
        }
    }

    pub fn iterate<I>(&self) -> I
    where
        I: IntoIterator<Item = &'a T>,
    {
        self.items.iter()
    }
}

This fails to compile, giving the error:

error[E0308]: mismatched types
  --> src/lib.rs:19:9
   |
15 |     pub fn iterate<I>(&self) -> I
   |                                 - expected `I` because of return type
...
19 |         self.items.iter()
   |         ^^^^^^^^^^^^^^^^^ expected type parameter, found struct `std::collections::hash_set::Iter`
   |
   = note: expected type `I`
              found type `std::collections::hash_set::Iter<'_, T>`
   = help: type parameters must be constrained to match other types
   = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters

The return value from HashSet::iter method appears to act like an Iterator when, for example within a for loop. It seems like some kind of type coercion might be possible in my iterate method, but clearly the compiler has other ideas.

How can I specify a return type of Iterator in a function while returning the return value from a HashSet iterator?

In other words, I want to delegate iteration to HashSet. I'd like to avoid implementing a custom iterator to do this, but if that's the only way would be interested in seeing how it's done. I've been able to get this working by returning a Box<dyn Iterator>, but I'd like to avoid that approach as well if it's possible to work just with trait boundaries as described in this answer.

One more requirement: the approach must work when the iterate method is declared on a trait. That appears to rule out an approach like:

pub trait FooTrait {
    fn iterate(&self) -> impl Iterator<Item=&T>;
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Rich Apodaca
  • 28,316
  • 16
  • 103
  • 129
  • From the duplicates: `fn iterate(&self) -> std::collections::hash_set::Iter<'_, T>` or `fn iterate(&self) -> Box + '_>`. – Shepmaster Dec 17 '19 at 14:47

1 Answers1

0

You want impl Iterator:

impl<'a, T: 'a + Eq + Hash> Foo<T> {
    // ...

    pub fn iterate(&'a self) -> impl Iterator<Item = &'a T> {
        self.items.iter()
    }
}

The Rust book has this section covering it. It is defined and refined in RFC 1522, RFC 1951, and RFC 2071. It isn't all done yet and has this tracking issue.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
edwardw
  • 12,652
  • 3
  • 40
  • 51
  • Thanks, but in trying to simplify the question, I left out an important requirement: the iterate method needs to be declarable on a trait. I updated my question. – Rich Apodaca Dec 17 '19 at 14:38