Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

trait with functions that return an iterator

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.

like image 921
ynimous Avatar asked Jul 08 '15 08:07

ynimous


People also ask

How do I return an iterator in C++?

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.

What are rust traits?

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.


2 Answers

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> {
        /* ... */
    }
}
like image 125
bluss Avatar answered Oct 11 '22 02:10

bluss


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() { }
}
like image 20
ynimous Avatar answered Oct 11 '22 02:10

ynimous