How to slice a vector without using its length?

I've a frames: Vec<Vec<u16>>, where the inner vector can have either 1 or 2 elements. I want to take 2 elements starting at index i, for which the following possibilities exist:

  1. frames[i] has 2 elements and there's no frames[i+1].
  2. frames[i] has 1 element and frames[i+1] has 1 element.
  3. frames[i] has 1 element and frames[i+1] has 2 elements.
  4. frames[i] has 2 elements and frames[i+1] has 1 element.
  5. frames[i] has 2 elements and frames[i+1] has 2 elements.

I've come up with the following options:

Option 1:

frames[i]
	.iter()
	.chain(frames.get(i + 1).unwrap_or(&vec![]).iter())
	.take(2)

Option 2:

frames
    [i..cmp::min(i + 2, frames.len())]
    .iter()
    .flat_map(|f| f.iter())
    .take(2)

It'd be nice if I could do frames[i..i + 2] (or the get call) without worrying about the length, but it throws out of bounds exception. Some other languages, like Python and Go, would return the remaining of the slice if the end index is out of bounds, but Rust doesn't. Is there a cleaner way to do this?

You can do:

frames
    [i..] // <-- no end bound here
    .iter()
    .flat_map(|f| f.iter())
    .take(2)
1 Like

To ensure you'll have no "out of bounds exception" (panic), you can adapt @tuxmain's code to avoid slicing and instead to use iterator methods only:

frames
    .iter()
    .skip(i)
    .flat_map(|f| f.iter())
    .take(2)
1 Like

I think that .flat_map(|f| f.iter()) can be replaced by .flatten() here (haven't tested it though).

Sadly, no, I just tried. flatten is for an Iterator<Iterator>> while we have here an Iterator<Vec>>.

It's Iterator<Item=IntoIterator>. This works:

    let iter = frames   // Vec<Vec<u16>>
        .get(i..)       // Option<&[Vec<u16>]>
        .into_iter()    // Iterator<Item=&[Vec<u16>]> (length 0 or 1)
        .flatten()      // Iterator<Item=&Vec<u16>>   (frames[i..].iter())
        .flatten()      // Iterator<Item=&u16>
        .take(2);
        
    for item in iter {
        println!("{item}");
    }

Playground.

5 Likes