Split a slice, but keep it contiguous?

Hi,

this following snippet

fn main() {
    let v: Vec<u32> = vec![1, 2, 3, 4, 5, 6];

    for i in v.split(|x| {
        return *x == 4;
    }) {
        println!("{:?}", i);
    }
}

(Playground)

doesn't give me the result I am after because it makes the 4 disappear.

I need to split a range while preserving every elements,keeping them contiguous.
So, instead of
[1,2,3] and [5,6],

I need
[1,2,3], [4,5,6]

Note: I cannot use chunk because the length of a slice is not known at compile time. And they differ (one may be 7 and the other 3).
In the example, the two slices of size 3 are just by luck.
How to achieve this ?

Thanks

There's also the split_inclusive method

Though it puts the matched item in the preceding slice

1 Like

Yes I saw that, my only hope is peekable with split to be able to see the next item without consuming it.
Then, with the inclusive it could work,
I will try tomorrow if something like this can be set up.

Use Iterator::position:

    let idx = v.iter().position(|&x| x == 4).unwrap_or(v.len());
    let (head, tail) = v.split_at(idx);
6 Likes

Nice! That will do,

Thanks!

Finally,

Here is what I ended up using:

let v: Vec<u32> = vec![1, 2, 3, 4, 5, 6, 4, 7, 9];

for i in v.as_slice().split_inclusive(|x| {

        let dist = (x - &v[0]) as usize;

        if dist < (v.len() - 1) {

                let next_elem = &v[dist + 1];

                if *next_elem == 4 {

                        return true;
                } else {

                        return false;
                }
        } else {

                return false;
        }
}) {

        println!("{:?}", i);
}

It works because a slice has contiguous memory.
An interface like

// without optional
for i in v.as_slice().split_inclusive_peek(|x_is_peeked_value| {
        if *x_is_peeked_value == 4 {

                return true;
        } else {

                return false;
        }
}) {

        println!("{:?}", i);
}

or

//with optional
for i in v.as_slice().split_inclusive_peek(|x_is_peeked_value_option| {

        match x_is_peeked_value_option {
                Some(e) => {
                        if *e == 4 {

                                return true;
                        } else {

                                return false;
                        }
                }
                None => return false
        }
}) {

        println!("{:?}", i);
}

would have been of a simpler use, avoiding the manual calculations. The parameter is the next value and we end up using the already existing inclusive version of the algorithm.

Just a nit:

and similar can be written as just

return *next_elem == 4;
1 Like

That's absolutely right, first block of code after the wake up :slight_smile:

Be careful with this, it will panic if the first element is a 4.

EDIT: It will panic if any element is smaller than a later element.

EDIT2: Here's a rust playground that won't panic, I don't know if it does anything useful when the numbers aren't how you expect them to be though.

1 Like

You are right, the first control is a nice addition,

Neat!
Thank you

1 Like

I would like to point out that

let dist = (x - &v[0]) as usize;

working has exactly nothing to do with slice being a contiguous memory, it works because slice has elements in ascending order with increment 1. @drmason13’s answer is no different which he acknowledges in EDIT2.

Here is a version which actually exploits slice being a contiguous memory, though I am really not sure whether it is completely correct. (This is also why I did not use offset_from instead.)

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.