We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Ownership II: Borrowing and the Borrow Checker step 5 of 24
Non-lexical lifetimes: a borrow ends at its last use
Fix the borrow error in this function:
pub fn rotate_and_report(xs: &mut [i64], k: usize) -> (i64, i64)
Rotate the slice left by k positions and return its first element before
and after the rotation. Wrap the rotation amount using k % xs.len().
For an empty slice, leave it empty and return (0, 0).
The tests check both the returned pair and the contents of the slice.
[10, 20, 30], k=0 -> (10, 10), slice [10, 20, 30]
[10, 20, 30], k=3 -> (10, 10), slice [10, 20, 30]
[10, 20, 30], k=7 -> (10, 20), slice [20, 30, 10]
[42], k=99 -> (42, 42), slice [42]
[], k=3 -> (0, 0), slice []
The starter already handles the empty slice and reduces the rotation amount.
Keep both checks: remainder by zero panics, and rotate_left panics when
its argument exceeds the slice length.
Why the starter fails
The starter saves &xs[0] in old. This is a reference to an element,
not a snapshot of its value. The return expression reads through that
reference after rotate_left has requested mutable access to the slice.
Rust rejects the overlapping shared and mutable borrows with E0502.
Here, the result needs the original number. Since i64 implements Copy,
let old = xs[0]; saves an independent value. Return that value directly
instead of dereferencing it. The saved number no longer borrows the slice.
A borrow can end before its block does
Rust uses non-lexical lifetimes (NLL) to determine where references must remain valid from how they are used in the program. In a simple sequence, a borrow can end after the reference's last use, even if the variable is still in scope:
fn main() {
let mut text = String::from("hi");
let first_view = &text;
println!("{first_view}"); // Last use of this reference.
text.push('!');
assert_eq!(text, "hi!");
}
The mutation is accepted because first_view is no longer needed.
If you print first_view again after push, the borrow must span the
mutation, and compilation fails.
This is a useful way to investigate borrow errors: find the conflicting references and their later uses. Sometimes moving a use earlier is enough; sometimes you need an owned value, as in this exercise. Branches, returned references, and destructors can impose additional lifetime requirements, so the last textual mention of a variable is not a universal rule.
RFC 2094 explains the motivation for NLL and the limitations of the older checker. For a related collection example, see the entry API lesson.
Borrow lifetimes and dropping values are different
NLL does not automatically destroy a value after its last ordinary use.
A lock guard, for example, normally keeps its lock until the guard is
dropped. Use a smaller scope or drop(guard) when you need to release it
earlier. Scopes remain useful for controlling resource lifetimes.
For this exercise, copying one i64 is enough. You do not need to clone
the slice or introduce another scope.
A passing submission must compile, pass the tests, and pass
clippy -D warnings.
Loading visualization…
Your code, saved here
Sign in to access your accepted solutions and saved draft history. You can keep reading the lesson without an account.
Sign in to your accountStuck?
Rust reference solution
Sign in to track your attempts toward unlocking the reference solution.