Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Regex capture iterator method moves iterator

I am processing simple strings of the format "1s:1d", "100:5000", etc with this regex:

let retention_matcher = regex::Regex::new({r"^(\d+)([smhdy])?:(\d+)([smhdy])?$"}).unwrap();

I know this regex should only match once so I want to run the regex for captures and check the number of captures.

    let iter = retention_matcher.captures_iter(ts);
    let count = iter.count();
    println!("iter.count(): {}", count);

    let _ : Vec<Option<(u64,u64)>> = iter.map(|regex_match| {
        let retval = retention_spec_to_pair(regex_match);
        println!("precision_opt: {:?}", retval);
        retval
    }).collect();

The issue is that the count() method moves iter and I can no longer use it.

src/bin/whisper.rs:147:42: 147:46 error: use of moved value: `iter`
src/bin/whisper.rs:147         let _ : Vec<Option<(u64,u64)>> = iter.map(|regex_match| {
                                                                ^~~~
src/bin/whisper.rs:144:21: 144:25 note: `iter` moved here because it has type `regex::re::FindCaptures<'_, '_>`, which is non-copyable
src/bin/whisper.rs:144         let count = iter.count();

This does not make sense to me. Should the count method just return a copyable usize value and not move iter? How can I work around this issue?

like image 892
xrl Avatar asked Jun 01 '15 08:06

xrl


1 Answers

I suspect that you're thinking of iter as being an abstract sequence of captures. It is more accurate to think of it as representing a position within an abstract sequence of captures. The basic thing any iterator knows to do is advance to the next item in a sequence; that is, you can advance the position and get the next element in the sequence.

count moves iter because in order to count how many elements are in the sequence, it has to generate the whole sequence. This necessarily modifies the iterator by stepping it through the whole sequence. It moves because after calling count, the iterator really isn't useful any more. By definition, it must be past the end of the sequence!

Sadly, it doesn't look like the iterator type in question (FindCaptures) is Clone, so you can't just make a copy of it.

The solution would be to restructure your code to not call count. If you want to get the first element and ensure there are no more, the simplest pattern would be:

let mut iter = ...;
if let Some(regex_match) = iter.next() {
    // Ensure that there is no next element.
    assert_eq!(iter.next(), None);

    // process regex_match ...
}
like image 163
DK. Avatar answered Sep 19 '22 06:09

DK.