Skip to content

← Ownership II: Borrowing and the Borrow Checker step 19 of 24

Medium Primitives

What a &mut parameter actually promises

Repair self_merge while keeping the provided merge_into unchanged:

pub fn merge_into(dst: &mut Vec<i64>, src: &[i64])
pub fn self_merge(xs: &mut Vec<i64>) -> usize

merge_into appends all source elements to the destination, preserving duplicates, then sorts the destination in ascending order. It does not change the source.

self_merge duplicates every element currently in its vector, sorts the result in ascending order and returns the new length. It works on the vector it receives, whether or not that vector is already sorted. Empty input stays empty and returns zero.

The harness first merges src into dst, records that intermediate result, and then calls self_merge on it:

Destination Source After merge_into After self_merge Length
[3, 1] [2] [1, 2, 3] [1, 1, 2, 2, 3, 3] 6
[5] [] [5] [5, 5] 2
[] [] [] [] 0

Values may be any i64; neither function performs arithmetic on them. Preserve all occurrences rather than deduplicating.

Why the starter call conflicts

The starter tries to use one vector as both arguments:

merge_into(xs, xs);

The first argument needs a mutable borrow of the vector. The second needs a shared slice of its elements for the same call. Rust rejects these overlapping borrows with E0502.

merge_into may grow the destination's allocation while reading the source. Its signature requires those accesses to be compatible; it cannot accept a source slice borrowed from that same destination. Reserving spare capacity would not make the conflicting call legal.

This restriction does not forbid all reborrowing. A function can derive a shorter borrow from a mutable reference and use it while the original access is suspended. What fails here is asking this API to use overlapping source and destination through separate arguments.

Use the vector's self-extension operation

Vec::extend_from_within(..) appends clones of the vector's original elements to its own end. For i64, cloning copies the value. The method manages any capacity growth without requiring the caller to supply an overlapping source slice.

After extending, sort the vector and return its length. There is no separate snapshot vector, but the destination may still need to allocate more capacity. This avoids a temporary collection, not every possible allocation.

Copying the original elements into a temporary vector and merging from that snapshot can also be correct. It is unnecessary for this task. Likewise, split_at_mut is useful for distinct regions of a slice, but it does not by itself duplicate a whole vector or grow its length.

Returned references can prolong an input borrow

A method returning a shared reference can still keep its receiver mutably borrowed. This signature:

fn get(&mut self) -> &Thing

has the same elided lifetime relationship as:

fn get<'a>(&'a mut self) -> &'a Thing

The caller must respect that exclusive input borrow while it continues to use the returned reference. The borrow can end at the reference's last use; it does not necessarily last until the binding's scope ends. For example:

struct Counter(i32);
impl Counter {
    fn get(&mut self) -> &i32 { &self.0 }
}

let mut counter = Counter(1);
let observed = counter.get();
assert_eq!(*observed, 1); // Last use of observed.
counter.0 = 2;
assert_eq!(counter.0, 2);

If observed were used again after the assignment, the overlapping access would be rejected. Choose &self when an API only needs shared access; choose &mut self when it needs exclusivity. Interior mutability and unsafe abstractions require their own access rules, so a lint alone is not a complete description of an API's guarantees.

What the intermediate output checks

Both helpers must sort independently. Checking the intermediate vector catches a missing sort in merge_into that the later sort in self_merge could hide. It does not check the original append order, because that intermediate result is already sorted.

Submissions must compile, pass the cases and pass the Rust Clippy gate.