For the index from end case you had the solution in the first version of the question, calculate the start index and use it instead. Of course you can do that programatically as well:

let h = "hellow world";
assert_eq!(h[h.len()-5..].to_owned(), "world".to_owned());

For the reverse case you got 2 options:

  • use rev() on a corresponding iterator:
for c in "hello world".chars().rev() {
    print!("{c}");
}
println!();

prints dlrow olleh

  • reverse the slice in place (which is complicated for utf-8 so I show it with bytes instead):
let s: &mut [u8] = &mut [1, 2, 3];
s.reverse();
assert_eq!(s, &[3, 2, 1]);

Playground link for all the examples in one go

Answer from cafce25 on Stack Overflow
๐ŸŒ
Rust
doc.rust-lang.org โ€บ std โ€บ primitive.slice.html
slice - Rust
Returns an iterator over the slice. The iterator yields all items from start to end.
๐ŸŒ
The Rust Programming Language
doc.rust-lang.org โ€บ book โ€บ ch04-03-slices.html
The Slice Type - The Rust Programming Language
When we find a space, we return a string slice using the start of the string and the index of the space as the starting and ending indices.
Discussions

Why can I start a slice past the end of a vector in Rust? - Stack Overflow
Notice: if the lower bound wasn't ... way to slice an empty Vec at all without causing a panic, which would be cumbersome. A simple way to think about it is "where the fence-post is put". ... Copy| 0 1 2 3 | ^ |- You are slicing from here. This includes the entire `Vec` (even if it was empty) ... Copy| 0 1 2 3 | ^ |- You are slicing from here to the end of the ... More on stackoverflow.com
๐ŸŒ stackoverflow.com
Why is slice end_index logic as it is?
I was doing rustlings ... sense, counting from 0th. Resulting in [1..TODO]. .. for me means 'through', confirmed by Bash: $ echo {1..3} 1 2 3 The Rust Book says: We create slices using a range within brackets by specifying [starting_index..ending_index]. a[3] == 4. ... More on users.rust-lang.org
๐ŸŒ users.rust-lang.org
7
0
February 5, 2023
Neater way to access the last n elements in a vec?

You can use an endless range:

let vec = vec![1, 2, 3, 4, 5];
println!("Remaining: {:?}", &vec[2..]);

Prints: "Remaining: [3, 4, 5]"

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=8b1a03af4ae475b294030f3d5d43b5ad

More on reddit.com
๐ŸŒ r/learnrust
14
18
January 15, 2020
Cleanest way to get the index of an element in a slice by pointer/reference?
let slice = &[1, 2, 3, 4]; let elem = &slice[2]; let slice_ptr = slice.as_ptr() as usize; let elem_ptr = elem as *const i32 as usize; let index = elem_ptr - slice_ptr; Results are unpredictable if elem is not a reference to an element in slice. More on reddit.com
๐ŸŒ r/rust
15
12
December 24, 2021
Top answer
1 of 2
19

This is simply because std::ops::RangeFrom is defined to be "bounded inclusively below".

A quick recap of all the plumbing: v[4..] desugars to std::ops::Index using 4.. (which parses as a std::ops::RangeFrom) as the parameter. std::ops::RangeFrom implements std::slice::SliceIndex and Vec has an implementation for std::ops::Index for any parameter that implements std::slice::SliceIndex. So what you are looking at is a RangeFrom being used to std::ops::Index the Vec.

std::ops::RangeFrom is defined to always be inclusive on the lower bound. For example [0..] will include the first element of the thing being indexed. If (in your case) the Vec is empty, then [0..] will be the empty slice. Notice: if the lower bound wasn't inclusive, there would be no way to slice an empty Vec at all without causing a panic, which would be cumbersome.

A simple way to think about it is "where the fence-post is put".

A v[0..] in a vec![0, 1, 2 ,3] is

|  0    1    2    3   |
  ^
  |- You are slicing from here. This includes the
     entire `Vec` (even if it was empty)

In v[4..] it is

|  0    1    2    3   |
                    ^
                    |- You are slicing from here to the end of the Vector.
                       Which results in, well, nothing.

while a v[5..] would be

|  0    1    2    3   |
                        ^
                        |- Slicing from here to infinity is definitely
                           outside the `Vec` and, also, the
                           caller's fault, so panic!

and a v[3..] is

|  0    1    2    3   |
                ^
                |- slicing from here to the end results in `&[3]`
2 of 2
6

While the other answer explains how to understand and remember the indexing behavior implemented in Rust standard library, the real reason why it is the way it is has nothing to do with technical limitations. It comes down to the design decision made by the authors of Rust standard library.

Given v = vec![1,2,3,4], why does v[4..] return an empty vector, but v[5..] panics [..] ?

Because it was decided so. The code below that handles slice indexing (full source) will panic if the start index is larger than the slice's length.

fn index(self, slice: &[T]) -> &[T] {
    if self.start > slice.len() {
        slice_start_index_len_fail(self.start, slice.len());
    }
    // SAFETY: `self` is checked to be valid and in bounds above.
    unsafe { &*self.get_unchecked(slice) }
}

fn slice_start_index_len_fail(index: usize, len: usize) -> ! {
    panic!("range start index {} out of range for slice of length {}", index, len);
}

How could it be implemented differently? I personally like how Python does it.

v = [1, 2, 3, 4]

a = v[4]   # -> Raises an exception        - Similar to Rust's behavior (panic)
b = v[5]   # -> Same, raises an exception  - Also similar to Rust's

# (Equivalent to Rust's v[4..])
w = v[4:]  # -> Returns an empty list      - Similar to Rust's
x = v[5:]  # -> Also returns an empty list - Different from Rust's, which panics

Python's approach is not necessarily better than Rust's, because there's always a trade-off. Python's approach is more convenient (there's no need to check if a start index is not greater than the length), but if there's a bug, it's harder to find because it doesn't fail early.

Although Rust can technically follow Python's approach, its designers decided to fail early by panicking in order that a bug can be faster to find, but with a cost of some inconvenience (programmers need to ensure that a start index is not greater than the length).

๐ŸŒ
Rust
docs.rs โ€บ ndarray โ€บ latest โ€บ ndarray โ€บ struct.Slice.html
Slice in ndarray - Rust
Slice::new(a, None, -1) is every element, from a until the end, in reverse order. It can also be created with Slice::from(a..).step_by(-1).
๐ŸŒ
Programiz
programiz.com โ€บ rust โ€บ slice
Rust Slice (With Examples)
A Rust slice is a data type used to access portions of data stored in collections like arrays, vectors and strings. ... Now, if we want to extract the 2nd and 3rd elements of this array. We can slice the array like this, ... [1..3] - is a notation for slicing the array from start_index 1 (inclusive) to end...
๐ŸŒ
DEV Community
dev.to โ€บ francescoxx โ€บ slices-in-rust-g6
Slices in Rust - DEV Community
March 5, 2024 - Rust has a built-in type called Slice that is used to reference a contiguous sequence of elements in a collection. Slices are a very important concept in Rust, and they are used extensively in the standard library.
Find elsewhere
๐ŸŒ
KoderHQ
koderhq.com โ€บ tutorial โ€บ rust โ€บ slice
Rust Slices Tutorial | KoderHQ
We create a slice by specifying from which index the slice should start, and at which index the slice should end between square brackets and separated by two dot operators.
๐ŸŒ
Codecademy
codecademy.com โ€บ docs โ€บ rust โ€บ slices
Rust | Slices | Codecademy
May 15, 2024 - The syntax for a slice in Rust is represented as &[T], where T is the element type. let my_slice = &data_structure[start_index..end_index]; data_structure: Refers to the chosen data structure.
๐ŸŒ
Rust
doc.rust-lang.org โ€บ std โ€บ slice โ€บ index.html
std::slice - Rust
An iterator over subslices separated by elements that match a predicate function, limited to a given number of splits, starting from the end of the slice.
๐ŸŒ
The Rust Programming Language
doc.rust-lang.org โ€บ nightly โ€บ core โ€บ slice โ€บ index.html
core::slice - Rust
An iterator over subslices separated by elements that match a predicate function, limited to a given number of splits, starting from the end of the slice.
๐ŸŒ
MIT
web.mit.edu โ€บ rust-lang_v1.25 โ€บ arch โ€บ amd64_ubuntu1404 โ€บ share โ€บ doc โ€บ rust โ€บ html โ€บ book โ€บ second-edition โ€บ ch04-03-slices.html
Slices - The Rust Programming Language
Internally, the slice data structure stores the starting position and the length of the slice, which corresponds to ending_index minus starting_index. So in the case of let world = &s[6..11];, world would be a slice that contains a pointer to the 6th byte of s and a length value of 5. Figure 4-6 shows this in a diagram. Figure 4-6: String slice referring to part of a String ยท With Rustโ€™s ..
๐ŸŒ
TutorialsPoint
tutorialspoint.com โ€บ rust โ€บ rust_slices.htm
Rust - Slices
For example, slices can be used to fetch a portion of a string value. A sliced string is a pointer to the actual string object. Therefore, we need to specify the starting and ending index of a String. Index starts from 0 just like arrays.
๐ŸŒ
Mastering Backend
blog.masteringbackend.com โ€บ rust-slices-cutting-into-references-the-safe-way
Rust Slices: How to Borrow Part of an Array, Vec, or String - Mastering Backend
August 27, 2026 - Slices are the borrowing half of a pattern that runs through the whole language. The owning type holds the data, the slice borrows a view of it, and conversions between the two are explicit. Our guide to Rust's From and Into traits covers the owning half of that story, including when to_vec is the right call and when a borrow is enough.
๐ŸŒ
Linux Hint
linuxhint.com โ€บ rust-slices
Rust Slices โ€“ Linux Hint
For example, if we are starting the slice from index 0, we can omit the 0 and just set the ending index.
๐ŸŒ
Rust Programming Language
users.rust-lang.org โ€บ help
Why is slice end_index logic as it is? - help - The Rust Programming Language Forum
February 5, 2023 - I was doing rustlings ... sense, counting from 0th. Resulting in [1..TODO]. .. for me means 'through', confirmed by Bash: $ echo {1..3} 1 2 3 The Rust Book says: We create slices using a range within brackets by specifying [starting_index..ending_index]. a[3] == 4. ...
๐ŸŒ
KodeKloud Notes
notes.kodekloud.com โ€บ ownership โ€บ the slice type
The Slice Type - KodeKloud
January 28, 2026 - The end index is exclusive, indicating where the slice ends. Remember that the range syntax helps prevent out-of-bounds errors by enforcing that the slice only covers valid indices of the collection.
๐ŸŒ
Reddit
reddit.com โ€บ r/learnrust โ€บ neater way to access the last n elements in a vec?
r/learnrust on Reddit: Neater way to access the last n elements in a vec?
January 15, 2020 -

I'm used to being able to do this in Python with my_list[-n:] but I can't find an equivalent for Rust.

I've been doing:

let index = my_vec.len() - (n+1);
let last_n = my_vec.get(index..);

but this seems messy. Is there a better way?

EDIT: My latest solution is my_vec.windows(n).last() which creates an iterator of overlapping subslices of length n and then takes the last element. Benchmarked it and it's 8% slower. Not sure if I'm going to notice that difference or not. I also tried my_vec.rchunks(n).nth(0) but that was 40% slower than my original method.