1

I have the following piece of code:

let mut lex_index = 0;
let chars = expression.chars();
while lex_index < chars.count() {
    if(chars[lex_index] == "something") {
        lex_index += 2;
    } else {
        lex_index += 1;
    }
}

I use a while loop here since I sometimes need to skip a char in chars. However, this gives me the following error:

error[E0382]: use of moved value: `chars`
  --> src/main.rs:23:15
   |
23 |     while i < chars.count() {
   |               ^^^^^ value moved here in previous iteration of loop
   |
   = note: move occurs because `chars` has type `std::str::Chars<'_>`, which does not implement the `Copy` trait
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
duck
  • 1,674
  • 2
  • 16
  • 26

2 Answers2

9

It's better to iterate over something instead of using an index:

let mut chars = "gravy train".chars().fuse();

while let Some(c) = chars.next() {
    if c == 'x' {
        chars.next(); // Skip the next one
    }
}

We fuse the iterator to avoid any issues with calling next after the first None is returned.


Your code has a number of issues:

  1. Iterator::count consumes the iterator. Once you've called that, the iterator is gone. That's the cause of your error. An alternate solution is to use Iterator::by_ref so that consuming the iterator you count isn't the end of the line.

  2. chars is of type Chars, which does not support indexing. chars[lex_index] is nonsensical.

  3. You cannot compare a char to a string, so chars[lex_index] == "something" wouldn't compile either. It's possible you could use Chars::as_str, but then you'd have to give up Fuse and deal with that yourself.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • Based on my interpretation of https://doc.rust-lang.org/std/str/struct.Chars.html, the Chars iterator is already fused. According to the documentation, calling fused() on an already fused operator has no effect and results no performance penalty. Was your use of ".fuse()" intentional? Maybe that aspect of the library has been updated since 15/07/17. – PersonWithName Jul 03 '19 at 09:15
  • @hddh you've basically answered your own question: *calling `fused()` on an already fused operator has no effect and results no performance penalty*. Always call `fuse` when you need the ability to call `next` after the iterator returns `None` once. – Shepmaster Jul 03 '19 at 11:53
2

You can use the strcursor crate for this:

extern crate strcursor;

fn main() {
    use strcursor::StrCursor;
    let expr = r"abc\xdef";
    let mut cur = StrCursor::new_at_start(expr);

    // `after`: the next grapheme cluster
    while let Some(gc) = cur.after() {
        if gc == "\\" {
            // Move right two grapheme clusters.
            cur.seek_next();
            cur.seek_next();
        } else {
            print!("{}", gc);
            cur.seek_next();
        }
    }
    println!("");
}

// Output: `abcdef`
DK.
  • 55,277
  • 5
  • 189
  • 162