Step 5 of 6
Mutable slices: &mut [T]
&mut v[a..b] lends part of a vector or array for changing. Its type is &mut [T]. A function that changes elements in place, without adding or removing any, should take &mut [T]: it then works on arrays, vectors, and parts of either.
fn reset(values: &mut [i32]) {
for v in values {
*v = 0;
}
}
fn main() {
let mut board = [5, 3, 8, 1, 9, 2];
reset(&mut board[2..4]);
println!("{:?}", board);
}
[5, 3, 0, 0, 9, 2]
Looping over a &mut [i32] gives a &mut i32 for each element, so you change it with *v, as in the borrowing module. A slice has a fixed length: you can change its elements, but you can't push or remove. A function that needs to grow a vector takes &mut Vec<T>.
Built-in methods that change a slice in place
.sort(), .reverse(), .swap(i, j) and .fill(x) are slice methods. They work on a whole vector or on part of one:
fn main() {
let mut leaderboard = vec![40, 95, 70, 10, 85];
leaderboard[..3].sort(); // sort only the first three
println!("{:?}", leaderboard);
leaderboard.reverse();
println!("{:?}", leaderboard);
leaderboard.swap(0, 4);
println!("{:?}", leaderboard);
}
[40, 70, 95, 10, 85]
[85, 10, 95, 70, 40]
[40, 10, 95, 70, 85]
Two mutable parts at once: split_at_mut
Two &mut slices of the same vector, even of parts that don't overlap, don't compile:
fn main() {
let mut v = vec![1, 2, 3, 4];
let left = &mut v[..2];
let right = &mut v[2..];
left[0] = 9;
right[0] = 8;
println!("{:?}", v);
}
error[E0499]: cannot borrow `v` as mutable more than once at a time
--> src/main.rs:4:22
|
3 | let left = &mut v[..2];
| - first mutable borrow occurs here
4 | let right = &mut v[2..];
| ^ second mutable borrow occurs here
5 | left[0] = 9;
| ------- first borrow later used here
|
= help: use `.split_at_mut(position)` to obtain two mutable non-overlapping sub-slices
The borrow checker doesn't reason about the numbers inside the brackets (in real code they're usually only known at run time), so it can't prove the two parts don't overlap; it treats each one as borrowing all of v. The help line names the fix. split_at_mut checks the position once and returns two &mut slices that are guaranteed not to overlap:
fn main() {
let mut teams = [10, 20, 30, 40];
let (red, blue) = teams.split_at_mut(2);
red[0] += 1;
blue[0] += 100;
println!("{:?}", teams);
}
[11, 20, 130, 40]
(Inside, split_at_mut uses a few lines of unsafe code that the standard library guarantees are correct. The unsafe module shows how such safe wrappers are built.)
Common mistakes
- Pushing to a
&mut [T]:error[E0599]: no method named `push` found for mutable reference `&mut [i32]`. Take&mut Vec<T>if the function must grow the vector. - Forgetting
*when assigning to elements in a loop. - Two
&mutslices of one vector at once (E0499): usesplit_at_mut.
Your task
Write shift_left, which moves every element one place to the left and puts the first element at the end: [1, 2, 3, 4] becomes [2, 3, 4, 1]. Empty and one-element slices stay as they are. Use .swap in a loop (and not the built-in rotate_left, which would do it for you).
Challenge 2
Write zero_negatives, which replaces every negative number in the slice with 0 and returns how many it replaced.
Challenge 3
Write swap_halves, which swaps the first half of a slice with the second half: [1, 2, 3, 4] becomes [3, 4, 1, 2]. With an odd length, the middle element stays where it is: [1, 2, 3, 4, 5] becomes [4, 5, 3, 1, 2]. Use .swap(i, j), or split_at_mut to get the two halves as separate &mut slices.
Previous: Array and Vec slices: &[T] Next: Slices in real code