Reputation: 2044
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
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][..]),
);
Upvotes: 1
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
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