Since this question appears on Google, but doesn't include a generic solution, I'm gonna add one. Along with an API update.
As of 2024, there is the unstable slice.get_many_mut() (tracking issue #104642), which accepts takes a [usize; N] and returns Result<[&mut T; N], _>:
#![feature(get_many_mut)] fn main() { let mut arr = [0, 1, 2, 3, 4, 5]; println!("{:?}", arr.get_many_mut([0, 1])); // It retains the order of the indices println!("{:?}", arr.get_many_mut([1, 0])); // You can do as many indices as you want: println!("{:?}", arr.get_many_mut([5, 0, 2, 3])); }
Since most people like aren't using Nightly and unstable features, then we'll need to roll our own solution for now (or find a library providing it).
The more general use case is likely to "get two mutable items". If we want to retain the order of the indices, while avoiding unsafe code, then it is definitely a lot easier to implement get_two_mut() than get_many_mut().
So let's implement a trait GetTwoMut. Lukas' answer already mentioned split_at_mut() which we are going to use.
In short, we want to split_at_mut() such that our &mut [T] turns into (&mut [T], &mut [T]) where each of the two slices contain one of the indices. Then we can mutably borrow one item from each slice:
use std::mem; pub trait GetTwoMut<T> { fn get_two_mut(&mut self, indices: [usize; 2]) -> Option<[&mut T; 2]>; } impl<T> GetTwoMut<T> for [T] { fn get_two_mut(&mut self, [mut index0, mut index1]: [usize; 2]) -> Option<[&mut T; 2]> { // Swap so `index1` is the largest index let swap = index0 > index1; if swap { mem::swap(&mut index0, &mut index1); } if index0 == index1 { // We cannot mutably borrow the same index twice return None; } else if self.len() <= index1 { // The largest index is out-of-bounds return None; } // Split the slices into two slice references, such // that `index0` is in `lhs` and `index1` is in `rhs` let (lhs, rhs) = self.split_at_mut(index0 + 1); let mut index1 = &mut rhs[index1 - index0 - 1]; let mut index0 = &mut lhs[index0]; // If the indices were swapped, so swap the items if swap { mem::swap(&mut index0, &mut index1); } Some([index0, index1]) } }
The only difference between get_two_mut() and get_many_mut() (when using [usize; 2]). Is that get_two_mut() returns an Option, while get_many_mut() returns a Result with a specific GetManyMutError. Otherwise their behavior is the same.
If unsafe code is allowed, then the implementation can be simplified significantly:
impl<T> GetTwoMut<T> for [T] { fn get_two_mut(&mut self, [index0, index1]: [usize; 2]) -> Option<[&mut T; 2]> { if index0 == index1 { return None; } else if self.len() <= index0.max(index1) { return None; } let index0 = unsafe { &mut *(&mut self[index0] as *mut T) }; let index1 = unsafe { &mut *(&mut self[index1] as *mut T) }; Some([index0, index1]) } }
Additionally, this can easily be reworked into a custom get_many_mut() if needed. However, I will leave that as an exercise to the reader if needed.
I do want to emphasis though, that you should NOT use the unsafe version without reason. If you're implementing a custom get_many_mut(), then feel to use it as a baseline. But verify the safety beyond the 1 minute I spend on writing it.
Here's a test case, that asserts that both get_two_mut() and get_many_mut() produces the same items and order:
#[test] fn test_get_two_mut_vs_get_many_mut() { let mut arr = [0, 1, 2, 3, 4, 5]; let cases = [ // Valid: [0, 1], [1, 0], [1, 3], [3, 1], [2, 3], [3, 2], [2, 4], [4, 2], [0, 5], [5, 0], // Same index: [0, 0], [2, 2], // Out of bounds: [0, 10], [10, 10], [0, 1000], [1000, 0], ]; for indices in cases { let get_two_mut = arr.get_two_mut(indices).map(|[a, b]| [*a, *b]); let get_many_mut = arr.get_many_mut(indices).map(|[a, b]| [*a, *b]).ok(); assert_eq!(get_two_mut, get_many_mut); } }