2
\$\begingroup\$

I have an array (or string) to parse and I want to examine each element sequentially. This is a perfect case for the iterators / consumers that Rust offers.

The problem arises when I have to access previously parsed data during iteration. For example, I need to subtract the item that was 5 places before the current item.

The best way I know to achieve this using idiomatic Rust is a sliding window Vec that keeps track of the items previously seen, all of which happens inside a fold operation.

A working code extract looks like the following:

fn main() {
   let string = "111222023333"; // the string to parse  
   let str_iter = string.trim_left().trim_right().chars();
   let mut last = 1 as usize;
   let mut digit = 0 as usize;
   let mut window: Vec<usize> = Vec::new();
   let target = 5;
   str_iter.fold(1, |mut val, c| {
      digit = c.to_digit(10).unwrap() as usize;
      window.push(digit);
      if window.len() > target 
        {last = window.remove(0);} 
      if digit == 0 
        {val -= last;}
      val
   });
}

I don't really like this as I have to use a Vec to store the values of size target; I could just use indexes like I would in C/C++ but that again feels a bit wrong and not idiomatic at all.

Any ideas on how to idiomatic achieve this without using an additional Vec?

\$\endgroup\$
1
  • \$\begingroup\$ Voting to close because this code looks like stub code, which is disallowed. Your code can only give 1, 0 or break (because of overflow), decides so statically, and you haven't specified what the point of doing so is. This seems better suited for Stack Overflow as-is. \$\endgroup\$
    – Veedrac
    Commented Feb 26, 2016 at 12:20

1 Answer 1

3
\$\begingroup\$
  • .trim_left().trim_right() is just .trim().

  • You might as well inline str_iter, since you only use it in one place. At least move it closer to the use-point.

  • Folds aren't the idiomatic loop primitive in Rust. In this case it would be much nicer to just use for.

    • This improves compiler analysis, and you find out that digit's initial assignment isn't getting read. You don't need digit outside the loop at all, and thus it doesn't even need to be mutable.
  • You're using too many type annotations. The one as usize after to_digit is enough, and even if you did need to annotate the others you should use let last: usize = 1 or let last = 1usize. Similarly, let the Vec's type parameter be inferred.

  • Your brace formatting is not Rusty.

  • Instead of dealing with window, just run two zipped offset parallel iterators. Indices work fine, too, and aren't unidiomatic: clearer, efficient code is always idiomatic.

\$\endgroup\$
2
  • \$\begingroup\$ Ok! -- thanks for the tips; but let's say that I want to have a variable jump point over the iteration. I kept the code short omitting some cases. For example if you see 0 then jump 5 places back, if you see 1 then jump 2 places back. The chaining iterator would not work there, would it? \$\endgroup\$
    – jtimz
    Commented Feb 29, 2016 at 2:41
  • \$\begingroup\$ @jtimz Just add the overflow to the front of one of the iterators. (0..target).map(|_| overflow).chain(iter()).zip(iter()) \$\endgroup\$
    – Veedrac
    Commented Feb 29, 2016 at 6:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.