Blue7
Blue7

Reputation: 2044

How do I split a Vec to return a tuple of the item at the given index and the rest of the vector?

I want to iterate through a vector, and get a mutable reference to each item, and a mutable slice to the rest of the vector, so I can use both every iteration. Something like:

e.g:

for index in 0..model.len() {
    let (item, rest): (&mut Item, &mut [Item]) = model.split_rest_mut(index);
    item.do_something(rest);
}

e.g [1,2,3,4,5,6].split_rest_mut(2) would be 3, [1,2,4,5,6].

I would like this to be as performant as possible.

It seems to be similar behaviour to split_at_mut, so I imagine this should be possible.

How would I go about doing this?

Upvotes: 0

Views: 428

Answers (3)

E_net4
E_net4

Reputation: 29972

With the exact signature split_rest_mut(usize) -> (&mut T, &[T]), that is impossible: for an index which is neither for the first element nor the last element, the remaining elements are not contiguous in memory, and so cannot coexist in a single slice.

Instead, such a function would have to return two slices: one for the leading part of the slice and another one for the trailing part.

+-------------+-------------+--------------+
|  ...leading | middle item |  ...trailing |
+-------------+-------------+--------------+

Its implementation can be built with a combination of split_at_mut and split_first_mut.

pub fn split_at_rest_mut<T>(x: &mut [T], index: usize) -> (&mut T, &mut [T], &mut [T]) {
    assert!(index < x.len());
    // split in two
    let (leading, trailing) = x.split_at_mut(index);
    // split second half in [value, ...rest]
    let (val, trailing) = trailing.split_first_mut().unwrap();
    (val, leading, trailing)
}

Using:

let mut v = vec![1, 2, 5, 7, 8, 9];

assert_eq!(
    split_at_rest_mut(&mut v, 2),
    (&mut 5, &mut [1, 2][..], &mut [7, 8, 9][..]),
);

Playground

Upvotes: 1

Masklinn
Masklinn

Reputation: 42207

How would I go about doing this?

Call split_at_mut and retrieve the last element of the first slice as your "current"?

Alternatively, use a slice pattern: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=67d007bac2a94ff2cdcfbf36cdc4da35

if let [head, tail@..] = &mut model[i..] {
    *head += 1;
    println!("{}, {:?}", head, tail);
}

edit:

e.g [1,2,3,4,5,6].split_rest_mut(2) would be 3, [1,2,4,5,6].

that's not possible. A slice is a contiguous buffer. You can have a "slice before", "focus", "slice after" using split_at_mut and split_first_mut or split_last_mut: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=14cd90a694ffcb976f6e774a96d5d2c6

fn focus<T>(s: &mut [T], i: usize) -> Option<(&mut [T], &mut T, &mut [T])> {
    let (head, tail) = s.split_at_mut(i);
    let (focus, tail) = tail.split_first_mut()?;
    Some((head, focus, tail))
}

If you want a non-contiguous sequence you need to look at something like ndarray, and even then I'm not sure they'd have such an odd collection.

Upvotes: 0

Thomas
Thomas

Reputation: 181725

It's not built in, but you can create it yourself easily enough based on Vec::split_at_mut:

fn split_rest_mut<T>(vec: &mut Vec<T>, mid: usize) -> (&mut T, &mut [T]) {
    let (left, right) = vec.split_at_mut(mid);
    (left.last_mut().expect("mid is 0"), right)
}

Usage:

    let (item, rest): (&mut Item, &mut [Item]) = split_rest_mut(&mut model, index);

Vec::split_at_mut is somewhat special because it returns two mutable references. Its implementation guarantees that those don't refer to the same values inside the vector, but it takes unsafe code to implement it. So while you could theoretically create your own unsafe implementation of split_rest_mut, its safer to base it on split_at_mut instead.

Upvotes: 1

Related Questions