Start learning free

LESSON 126 OF 679 · ARRAYS - CHALLENGES

Drop the Ends in Rust: Slicing the Middle of a Vec

Practice Read the full explanation

You get a Vec of integers nums. Return a Vec with every number except the first and the last.

The numbers in between keep their order. When nums has only two numbers, return an empty Vec.

 

Example 1:

Input: nums = [4, 8, 15, 16, 23]
Output: [8, 15, 16]
Explanation: 4 is the first number and 23 is the last.
Both are left out.

Example 2:

Input: nums = [9, 0, 9]
Output: [0]
Explanation: Both 9s are ends.
Only the 0 is between them.

Example 3:

Input: nums = [1, 2]
Output: []
Explanation: 1 and 2 are the ends.
Nothing is between them.

 

Constraints:

  • nums has 2 to 10 numbers.
  • Every number is an integer from -1000 to 1000.
STEP 1 OF 2
    Ctrl + Enter to check

    RUST · ARRAYS - CHALLENGES

    Drop The Ends in Rust, explained

    Problem overview

    The function drop_ends receives a vector of integers, nums, with at least two numbers. It returns a new vector with every number except the first and the last, keeping the order of those in between. When there are exactly two numbers, nothing lies between the ends, so the result is empty.

    For [4, 8, 15, 16, 23] the ends are 4 and 23, so the answer is [8, 15, 16]. For [9, 0, 9] both 9s are ends and only the 0 remains.

    Approach

    Index 0 is the first number and index nums.len() - 1 is the last. The numbers worth keeping are at indexes 1 through len - 2. A range 1..nums.len() - 1 expresses that exactly, because a Rust range includes its start and leaves out its end. So the start of 1 skips the first number, and the end of len - 1 skips the last one.

    Step by step

    1. Build the range 1..nums.len() - 1 and slice nums with it. For five numbers that is 1..4, which covers indexes 1, 2 and 3.
    2. Copy the slice into an owned vector with to_vec(), and store it in middle. A bare slice only borrows from nums, so it can't be returned as a Vec.
    3. End the function with middle, without a semicolon, so Rust returns it.

    The solution in Rust

    impl Solution {
        fn drop_ends(nums: Vec<i32>) -> Vec<i32> {
            let middle = nums[1..nums.len() - 1].to_vec();
            middle
        }
    }
    

    With exactly two numbers the range is 1..1, which is empty, so middle is an empty vector and no special case is needed. The expression nums.len() - 1 is a usize, and it is safe here only because the constraints promise at least two numbers.

    Following one call

    For nums = [4, 8, 15, 16, 23], the length is 5, so the range is 1..4. It selects the items at indexes 1, 2 and 3, which are 8, 15 and 16. Index 0 (the 4) and index 4 (the 23) are never part of it. The input vector is not changed; the function copies the middle part into a new vector and returns that. Thinking in terms of the range's two ends makes the code easy to check: the start says how many items to skip at the front, and the distance from the end of the range to the length says how many to skip at the back.

    Edge cases and common mistakes

    • A vector with fewer than two numbers. With one number, the range becomes 1..0, and Rust panics with "slice index starts at 1 but ends at 0". With an empty vector, nums.len() - 1 would underflow the unsigned length (a panic in a debug build, a wrapped huge number in an optimized one). The lesson rules both out.
    • Using 1..nums.len(). That drops only the first number and keeps the last.
    • Using 0..nums.len() - 1. That drops only the last number and keeps the first.

    Testing

    Results from running the function:

    • [4, 8, 15, 16, 23] gives [8, 15, 16].
    • [9, 0, 9] gives [0].
    • [1, 2] gives [].
    Keep going with Rust500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free