Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

The Rust Strawberry test

One fine day at work, I came across a piece of code in Rust that looked like this:

fn main() {
   // Will this panic or not?
   let s : Vec<u8> = vec![0u8; 32];
   println!("{:?}", &s[1..=0]);
}

Initially, I thought, Oh no, this will panic! So, I threw this sample into the Rust playground to test it out. And… it returned an empty slice!!! NO PANIC.

Let’s dig into why. &s[1..=0] breaks down into the SliceIndex implementation for RangeInclusive:

#![allow(unused)]
fn main() {
unsafe impl<T> const SliceIndex<[T]> for ops::RangeInclusive<usize> {
    type Output = [T];

    #[inline]
    fn get(self, slice: &[T]) -> Option<&[T]> {
        if *self.end() == usize::MAX { None } else { self.into_slice_range().get(slice) }
    }

    #[inline]
    fn get_mut(self, slice: &mut [T]) -> Option<&mut [T]> {
        if *self.end() == usize::MAX { None } else { self.into_slice_range().get_mut(slice) }
    }

    #[inline]
    unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] {
        // SAFETY: the caller has to uphold the safety contract for `get_unchecked`.
        unsafe { self.into_slice_range().get_unchecked(slice) }
    }

    #[inline]
    unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut [T] {
        // SAFETY: the caller has to uphold the safety contract for `get_unchecked_mut`.
        unsafe { self.into_slice_range().get_unchecked_mut(slice) }
    }

    #[inline]
    fn index(self, slice: &[T]) -> &[T] {
        ...
        if end < len {
            end = end + 1;
            ...
        }
        slice_index_fail(start, end, slice.len())
    }

    #[inline]
    fn index_mut(self, slice: &mut [T]) -> &mut [T] {
        ...
        if end < len {
            end = end + 1;
            ...
        }
        slice_index_fail(start, end, slice.len())
    }
}

In this implementation, index and index_mut handle the end + 1, while the rest delegate it to into_slice_range, which handles it properly as well:

#![allow(unused)]
fn main() {
impl RangeInclusive<usize> {
    #[inline]
    pub(crate) const fn into_slice_range(self) -> Range<usize> {
        let exclusive_end = self.end + 1;
        let start = if self.exhausted { exclusive_end } else { self.start };
        start..exclusive_end
    }
}
}

So, in short, despite my intuition, this code is safe. ✅

But what do the LLMs think?

Well, the results reminded me of the infamous Strawberry incident. For the sake of uniformity, all prompts were tested using free accounts on their respective platforms.

LLMPanic or No Panic
ChatGPT✅ Panic
Claude✅ Panic
DeepSeek✅ Panic
Gemini❌ No Panic
Grok✅ Panic
Kimi K2.5✅ Panic

ChatGPT - Panic!

Link to chat

Yes — this will panic at runtime.

Why?

You created:

#![allow(unused)]
fn main() {
let s: Vec<u8> = vec![0u8; 32];
}

So s has length 32.

Then you slice it with:

#![allow(unused)]
fn main() {
&s[1..=0]
}

This is an inclusive range from index 1 to index 0.

That means:

  • start = 1
  • end = 0
  • inclusive range (..=)

In Rust, slicing requires:

start <= end + 1   (for inclusive ranges this effectively means start <= end)

But here:

1 > 0

So the range is invalid.

What happens?

Rust performs bounds checking at runtime for slices. Since the start index is greater than the end index, it triggers:

thread 'main' panicked at 'slice index starts at 1 but ends at 0'

Important Detail

Even though both indices (0 and 1) are individually valid indices for a 32-element vector, the range itself is invalid, which causes the panic.


Contrast Example (This does NOT panic)

#![allow(unused)]
fn main() {
&s[1..1]   // empty slice, valid
}

This produces an empty slice because the start equals the end.


Final Answer

Yes, it panics at runtime because the slice range 1..=0 is invalid (start > end).

Claude - Panic!

Link to chat

This code will panic at runtime.

Here’s why: &s[1..=0] is an inclusive range (notice the =), which means it’s trying to create a slice from index 1 up to and including index 0. This is logically impossible since you can’t have a range where the start index (1) is greater than the end index (0).

When you use an inclusive range start..=end, Rust requires that start <= end. In this case, 1 <= 0 is false, so the indexing operation will panic with an error like:

thread 'main' panicked at 'range start index 1 out of range for slice of length 0'

or similar message about the range being invalid.

If you wanted an empty slice, you could use:

  • &s[0..0] (exclusive range, valid)
  • &s[1..1] (exclusive range, valid)

But &s[1..=0] is invalid because inclusive ranges cannot have start > end.

DeepSeek - Panic!

Link to chat

Let’s analyze this code step by step:

fn main() {
   // Will this panic or not?
   let s : Vec<u8> = vec![0u8; 32];
   println!("{:?}", &s[1..=0]);
}

This code will panic at runtime.

Explanation:

The slice &s[1..=0] is trying to create a range from index 1 to index 0 (inclusive). This is an invalid range because:

  1. 1..=0 means “from 1 up to and including 0”
  2. This is impossible since the start index (1) is greater than the end index (0)

When Rust encounters an invalid range like this during indexing operations, it will panic with a message similar to:

thread 'main' panicked at 'slice index starts at 1 but ends at 0'

Important Note:

Even though the vector has 32 elements (indices 0-31), the slice operation fails before it even checks if the indices are within bounds. The range itself is invalid because start > end for an inclusive range.

The only non-panicking behavior with invalid ranges occurs with exclusive ranges (..) where an empty range is valid (e.g., &s[1..1] would give an empty slice without panicking).

Gemini - no Panic!

Link to chat

When I initially performed the test, Gemini did indeed say it would panic!. However, it was on an account whose settings did not allow me to share the chat, and repeating the test on new accounts has always yielded no panic. Interpret this however you will.

The short answer: No, it will not panic.

While it looks like you are trying to slice “backward” (from index 1 to 0), Rust handles this specific case gracefully because of how inclusive ranges work.

Why it works

In Rust, an inclusive range start..=end is considered empty if start > end. When you slice a Vec or a slice with an empty range, Rust returns an empty slice rather than panicking, provided the starting index is within the bounds of the container.

  • The Container: s has a length of 2 (indices 0 and 1).

  • The Slice: &s[1..=0]

  • The start index 1 is less than or equal to the length of the vector, so the starting point is valid.

  • Since 1 > 0, the range 1..=0 contains no elements.

  • The Result: You get an empty slice: [].


Comparison: When would it panic?

To give you some perspective, here is where the code would break:

ScenarioCodeResultWhy?
Valid Empty Slice&s[1..=0][]Start index is within bounds; range is empty.
Out of Bounds&s[3..=0]PanicThe start index 3 is greater than the length 2.
Inclusive vs. Exclusive&s[0..=2]PanicIndex 2 is out of bounds for an inclusive range (max index is 1).

Output

If you run your code, the console will print:

#![allow(unused)]
fn main() {
[]

}

Would you like to see how this behavior changes if you use an exclusive range like &s[1..0], or should we look at how to safely handle slicing with the .get() method?

Grok - Panic!

Link to chat

Yes, this code will panic.

The expression &s[1..=0] creates an invalid slice range in Rust.

Why it panics

In Rust, for a range start..=end (inclusive range):

  • it is only valid when start ≤ end
  • when start > end, the range is emptybut only if you use ..= with integers that satisfy start ≤ end + 1

But here:

#![allow(unused)]
fn main() {
1 ..= 0
}

→ 1 > 0
→ this is an invalid inclusive range

Rust does not allow creating an inclusive range where the start is greater than the end — it panics at runtime when you try to use it in a slice.

Quick comparison

#![allow(unused)]
fn main() {
&vec[1..0]     // this is OK → empty slice    (non-inclusive)
&vec[1..=0]    // this PANICS                 (inclusive, but 1 > 0)
}

What actually happens

#![allow(unused)]
fn main() {
let s: Vec<u8> = vec![0u8; 32];
println!("{:?}", &s[1..=0]);   // ← panic here
}

Output:

thread 'main' panicked at 'range start is greater than end'

(or very similar message depending on Rust version)

Correct ways to write an empty slice

#![allow(unused)]
fn main() {
&s[1..1]       // classic way, always empty
&s[5..5]
&s[..0]        // from beginning to 0 → empty
&vec![0u8;32][10..=9]   // this also panics (same reason)
}

So short answer:

Yes — your code panics.
Because 1..=0 is an invalid inclusive range.

Want to see many ways to get empty slices safely? 😄

Kimi K2.5 - Panic!

Link to chat

This is an interesting Rust question about slice indexing behavior. Let me analyze this code without needing any tools.

Analysis

The code creates a vector of 32 zeros, then tries to print &s[1..=0].

The key question: Will this panic or not?

Answer: Yes, this will panic! 🚨

Here’s why:

The Range 1..=0 (Inclusive Range)

In Rust, range bounds have specific rules:

  • start..=end is an inclusive range
  • The critical rule: The start must be ≤ end for the range to be valid

When you write 1..=0:

  • Start = 1
  • End = 0 (inclusive)

Since 1 > 0, this violates the range invariant that start ≤ end.

What Happens

#![allow(unused)]
fn main() {
let s: Vec<u8> = vec![0u8; 32];
println!("{:?}", &s[1..=0]);  // PANIC!
}

Panic message:

thread 'main' panicked at 'slice index starts at 1 but ends at 0'

Why Not an Empty Slice?

You might think “1 to 0 is just empty,” but Rust’s slice indexing doesn’t work that way. The bounds are checked before considering what elements would be selected. The range 1..=0 is considered invalid because:

  • Start (1) > End (0)
  • This is always an error, regardless of the vector’s contents

Contrast with Valid Empty Ranges

These would not panic (they produce empty slices):

#![allow(unused)]
fn main() {
&s[0..0]      // start == end, valid empty range
&s[1..1]      // start == end, valid empty range  
&s[32..32]    // start == end == len, valid empty range
&s[1..0]      // wait... this also panics! (exclusive range with start > end)
}

Even 1..0 (exclusive) panics for the same reason.

Summary

ExpressionResult
s[0..0]✅ Empty slice &[]
s[1..1]✅ Empty slice &[]
s[1..0]Panic (start > end)
s[1..=0]Panic (start > end)

The panic occurs because Rust validates that range bounds are logically ordered before applying them to the slice.