Skip to Content
Course content

91: Mock Coding Interview Walkthrough in Rust

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Coding interviews aren't just about getting the right answer; they're about how you communicate your thought process while navigating a language's constraints. In Rust, this is particularly interesting because the borrow checker often forces you to be more explicit about your data ownership than you would be in Python or Java. Let's walk through a classic: Merge Intervals.

The problem is simple: given a collection of intervals (like [1, 3] and [2, 6]), merge all overlapping intervals (resulting in [1, 6]). If I were sitting in an interview right now, here is exactly how I'd handle it.

Talking through the strategy

Before I touch the keyboard, I'd tell the interviewer: "The trick here is that if the intervals are sorted by their start times, we only need to look at the last interval we've processed to know if the current one overlaps." I'm establishing a linear time complexity after a sort, which is O(n log n). In Rust, I'll want to use a Vec to store my results because we don't know the final size of the merged list upfront.

Handling the initial sort

First, I need to make sure the input is sorted. I'll assume the input is a Vec<Vec<i32>>. I'll use sort_by_key because it's more concise than a full closure when I only care about the first element.

fn merge_intervals(mut intervals: Vec<Vec<i32>>) -> Vec<Vec<i32>> {
    if intervals.is_empty() {
        return vec![];
    }

    // Sort by the start time of each interval
    intervals.sort_by_key(|i| i[0]);
    
    let mut merged: Vec<Vec<i32>> = Vec::new();
    // ... logic goes here
}

The logic loop and a common borrow-checker stumble

Now, I'll iterate through the sorted intervals. My goal is to check if the current interval starts before the previous one ends.

    for interval in intervals {
        if merged.is_empty() || interval[0] > merged.last().unwrap()[1] {
            merged.push(interval);
        } else {
            // Update the end time of the last merged interval
            merged.last_mut().unwrap()[1] = merged.last().unwrap()[1].max(interval[1]);
        }
    }
    merged

Wait. If I try to compile this, I'm going to hit a wall. Look at that else block. I'm calling merged.last_mut() and then immediately calling merged.last(). I'm trying to borrow the same vector as both mutable and immutable at the same time. The compiler will scream at me.

This is a great "interview moment." Instead of panicking, I'll explain to the interviewer: "I've accidentally created a overlapping borrow. I'm trying to read the end time while I'm mutating it. I can fix this by capturing the value I need first."

Refining the mutation

To fix this, I'll simply calculate the new end time before I perform the mutable borrow, or just use the mutable reference to perform the update directly. Let's do it the clean way:

    for interval in intervals {
        if merged.is_empty() || interval[0] > merged.last().unwrap()[1] {
            merged.push(interval);
        } else {
            // Get a mutable reference to the last element
            let last = merged.last_mut().unwrap();
            // Update it in place using the max of the current end and the new end
            last[1] = last[1].max(interval[1]);
        }
    }

By assigning merged.last_mut().unwrap() to a variable last, I'm holding a single mutable borrow. I can then access last[1] to both read and write. This is much more idiomatic. I've avoided the double-borrow, and the logic remains O(n) for the traversal.

Final Polish

In a real interview, I'd finish by discussing edge cases. What if the input is empty? (I handled that at the top). What if an interval is entirely contained within another? (The .max() call handles that). I'd also mention that if I wanted to avoid allocating a new Vec, I could potentially do this in-place, but that would make the code significantly more complex to read, and in an interview, readability usually wins over micro-optimizations unless specifically asked.




📋 Practical Task

Implement a Meeting Room Conflict Checker

Using the logic we discussed in the merge intervals walkthrough, write a function called has_conflict. This function should take a Vec<Vec<i32>> representing meeting start and end times. Instead of merging them, the function should return true the moment it detects that any two meetings overlap, and false if all meetings are distinct. Remember to sort the meetings first!

Rating
0 0

There are no comments for now.

to be the first to leave a comment.