Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to call count on an iterator and still use the iterator's items?

Tags:

rust

parts.count() leads to ownership transfer, so parts can't be used any more.

fn split(slice: &[u8], splitter: &[u8]) -> Option<Vec<u8>> {
    let mut parts = slice.split(|b| splitter.contains(b));

    let len = parts.count(); //ownership transfer

    if len >= 2 {
        Some(parts.nth(1).unwrap().to_vec())
    } else if len >= 1 {
        Some(parts.nth(0).unwrap().to_vec())
    } else {
        None
    }
}

fn main() {
    split(&[1u8, 2u8, 3u8], &[2u8]);
}
like image 633
std Avatar asked Jan 30 '15 10:01

std


People also ask

How do I keep iterators counting in Python?

To get the length of an iterator in Python:Use the list() class to convert the iterator to a list. Pass the list to the len() function, e.g. len(list(gen)) . Note that once the iterator is converted to a list, it is exhausted.

Can you use an iterator twice?

Iterators can generally not be iterated twice because there might be a cost to their iteration. In the case of str::lines , each iteration needs to find the next end of line, which means scanning through the string, which has some cost.

How do you count Iterables?

In most cases, the Iterable will be an instance of Collection, such as a List or a Set. In such cases, we can check the type of the Iterable and call size() method on it to get the number of elements. The call to size() is usually much faster than iterating through the entire collection.

Which method consumes an iterator?

To fix this warning and consume the iterator, we'll use the collect method, which we used in Chapter 12 with env::args in Listing 12-1. This method consumes the iterator and collects the resulting values into a collection data type.


2 Answers

It is also possible to avoid unnecessary allocations of Vec if you only need to use the first or the second part:

fn split<'a>(slice: &'a [u8], splitter: &[u8]) -> Option<&'a [u8]> {
    let mut parts = slice.split(|b| splitter.contains(b)).fuse();

    let first = parts.next();
    let second = parts.next();

    second.or(first)
}

Then if you actually need a Vec you can map on the result:

split(&[1u8, 2u8, 3u8], &[2u8]).map(|s| s.to_vec())

Of course, if you want, you can move to_vec() conversion to the function:

second.or(first).map(|s| s.to_vec())

I'm calling fuse() on the iterator in order to guarantee that it will always return None after the first None is returned (which is not guaranteed by the general iterator protocol).

like image 126
Vladimir Matveev Avatar answered Oct 18 '22 22:10

Vladimir Matveev


The other answers are good suggestions to answer your problem, but I'd like to point out another general solution: create multiple iterators:

fn split(slice: &[u8], splitter: &[u8]) -> Option<Vec<u8>> {
    let mut parts = slice.split(|b| splitter.contains(b));
    let parts2 = slice.split(|b| splitter.contains(b));

    let len = parts2.count();

    if len >= 2 {
        Some(parts.nth(1).unwrap().to_vec())
    } else if len >= 1 {
        Some(parts.nth(0).unwrap().to_vec())
    } else {
        None
    }
}

fn main() {
    split(&[1u8, 2u8, 3u8], &[2u8]);
}

You can usually create multiple read-only iterators. Some iterators even implement Clone, so you could just say iter.clone().count(). Unfortunately, Split isn't one of them because it owns the passed-in closure.

like image 31
Shepmaster Avatar answered Oct 18 '22 22:10

Shepmaster