Find next index of an element in vector

Hi, i want non-short circuit position().

below code not working, but you can see what i am trying to do.

fn main() 
{
  let test = vec!['a', 'b', 'c', '\r', 'd', 'e', 'f', 'k', '\r', 'g', 'h', 'i', '\r'];
 
  let mut i = 0; 
  while i < 3 
  {
    let index = test.iter().position(|&e| e == '\r').unwrap();
    println!("index: {}", index);
    i += 1;
  }
}

So every time you do vector.iter(), you get a new iterator that starts from the beginning, thus all the position() called on that will just search from the beginning, thus getting only the first '\r'.

What I would normally do(there should be more idiomatic ways) would just be

fn main() 
{
  let test = vec!['a', 'b', 'c', '\r', 'd', 'e', 'f', 'k', '\r', 'g', 'h', 'i', '\r'];
 
  for (index, &c) in test.iter().enumerate() {
      if c == '\r' {
          println!("index: {}", index);
      }
  }
}

Note that you cannot guarantee the generated assembly code is not short circuiting in Rust(though the compiler shouldn't be smart enough to do short circuiting in non-trivial cases).

But if you are relying on this property to be absolutely true for things like cryptographic systems, then you need to verify the assmemly code.

EDIT:
Even if the assembly code is not short circuiting, there are still caveats around whether the code is truly non-short circuiting or not, but that's as best as you can normally do.

2 Likes

Sorry I misunderstood your question, below would be the right position function

fn position_no_short_circuit(slice : &[char], target : char) -> Option<usize> {
  let mut res = None;
  for (index, &c) in slice.iter().enumerate() {
      if c == target {
          if let None = res {
            res = Some(index);
          }
      }
  }
  res
}

fn main() 
{
  let test = vec!['a', 'b', 'c', '\r', 'd', 'e', 'f', 'k', '\r', 'g', 'h', 'i', '\r'];
 
  println!("index: {}", position_no_short_circuit(&test, '\r').unwrap());
}

link to playground

It seems like @sailfish009 wants an iterator/cursor. I’d just write it as:

let iter = test.iter()
        .enumerate()
        .filter_map(|e| if *e.1 == '\r' { Some(e.0) } else { None });

You can apply other iterator combinators to it or loop over it manually or whatever. It’s lazy so it’ll advance as it’s driven by external code.

2 Likes