I'm trying to build a trait with functions that return an iterator.
My simple example looks like this:
pub trait TraitA {
fn things(&self) -> Iterator<Item=&u8>;
}
fn foo<A: TraitA>(a: &A) {
for x in a.things() { }
}
Which does not work because the Iterator size type is not known at compile time.
In C++, the iterator is designed to behave like a super-pointer. Thus, it usually "points" to the value, and using the operator ++, --, etc. (depending on the exact type of the iterator), you can move the iterator to "point" to the next, previous, etc. value in the container.
A trait in Rust is a group of methods that are defined for a particular type. Traits are an abstract definition of shared behavior amongst different types. So, in a way, traits are to Rust what interfaces are to Java or abstract classes are to C++. A trait method is able to access other methods within that trait.
Rust's libstd has one implementation of this, the trait IntoIterator
.
/// Conversion into an `Iterator`
pub trait IntoIterator {
/// The type of the elements being iterated
type Item;
/// A container for iterating over elements of type `Item`
type IntoIter: Iterator<Item=Self::Item>;
/// Consumes `Self` and returns an iterator over it
fn into_iter(self) -> Self::IntoIter;
}
The trait has this peculiar by-value (self
) formulation exactly to be able to express both “into iterator” and “borrow iterator” semantics.
Demonstrated by HashMap's IntoIterator
implementations. (They use the hashmap's iterator structs Iter
and IntoIter
.) What's interesting here is that the trait is implemented for the type &HashMap<K, V, S>
to express the “borrow iterator”.
impl<'a, K, V, S> IntoIterator for &'a HashMap<K, V, S>
where K: Eq + Hash, S: HashState
{
type Item = (&'a K, &'a V);
type IntoIter = Iter<'a, K, V>;
fn into_iter(self) -> Iter<'a, K, V> {
self.iter()
}
}
impl<K, V, S> IntoIterator for HashMap<K, V, S>
where K: Eq + Hash, S: HashState
{
type Item = (K, V);
type IntoIter = IntoIter<K, V>;
/// Creates a consuming iterator, that is, one that moves each key-value
/// pair out of the map in arbitrary order. The map cannot be used after
/// calling this.
fn into_iter(self) -> IntoIter<K, V> {
/* ... */
}
}
Based on another question, I thought that the best way to do it would be to define the Iterator as a trait type, like so:
pub trait TraitA<'a> {
type I1: Iterator<Item=u8>;
type I2: Iterator<Item=&'a u8>;
fn iter_i1(&self) -> Self::I1;
fn iter_i2(&self) -> Self::I2;
}
fn foo<'a, A: TraitA<'a>>(a: &A) {
for x in a.iter_i1() { }
for x in a.iter_i2() { }
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With