Slices of Vec<T> in Rust: How Do They Work and Why Do You Need Them?
\n\nIf you've ever written in Rust and asked yourself "How do I get a slice of a Vec<T> in Rust?" — you are not alone. This is one of the most popular questions on Stack Overflow, with thousands of views. And for good reason: working with vectors and their slices is a fundamental skill without which no serious Rust project is conceivable.
\n\nIn this article, we won't just show you how to get a slice. We'll break down the best practices of 2025, learn to avoid common pitfalls, and understand why Rust makes us think so much about borrowing. Let's go!
\n\nWhat is a slice in Rust?
\n\nBefore taking a slice, let's understand what it is. A slice is a reference to a contiguous sequence of elements in memory. In Rust, a slice is denoted as &[T]. It is not an owning data structure — it simply "looks at" a part of data belonging to someone else (for example, a vector or an array).
The main advantage of slices is safety. The Rust compiler guarantees that you won't go out of memory bounds, and the borrowing system won't allow you to modify data while there are active references to it.
\n\nWhy not use the vector directly?
\n\nA vector (Vec<T>) is an owning structure. If you pass it to a function, you either move it (losing access) or pass a reference. Slices give you flexibility: you can pass only a part of the data without copying it and without losing control over the original vector.
For example, you are working with a large data array and want to pass a fragment of it to a function for analysis. A slice is the ideal tool: it is lightweight (just two words: a pointer and a length) and safe.
\n\nMain ways to get a slice of Vec<T>
\n\nLet's move on to practice. There are several ways to get a slice from a vector. We'll look at the most popular and convenient ones.
\n\n1. Indexing operator: &vec[start..end]
\n\nThe most straightforward way is to use the range operator ..:
let vec = vec![1, 2, 3, 4, 5];\nlet slice: &[i32] = &vec[1..4]; // slice from index 1 to 4 (excluding 4)\nprintln!("{:?}", slice); // [2, 3, 4]\n\nThis method uses runtime bounds checking. If you go out of the vector's bounds, the program will panic. This is fine for learning examples, but in production code, you should be more careful.
\n\nYou can use various forms of ranges:
\n\n- \n
&vec[..]— the entire vector \n&vec[2..]— from index 2 to the end \n&vec[..3]— from the beginning to index 3 (excluding) \n&vec[1..=3]— from 1 to 3 inclusive \n
2. The .as_slice() method
\n\nThis method returns a slice of the entire vector. It is safer than &vec[..] because it explicitly expresses the intention:
let vec = vec![10, 20, 30];\nlet slice: &[i32] = vec.as_slice();\nprintln!("{:?}", slice); // [10, 20, 30]\n\nThe as_slice() method is especially useful when you want to emphasize that you are working with a slice, not a vector. This makes the code more readable and self-documenting.
3. Getting a mutable slice: as_mut_slice()
\n\nIf you need to modify elements inside the slice (but not the vector itself), use as_mut_slice():
let mut vec = vec![1, 2, 3];\nlet slice: &mut [i32] = vec.as_mut_slice();\nslice[0] = 100;\nprintln!("{:?}", vec); // [100, 2, 3]\n\nNote: the vector must be declared as mut. Also, you cannot have an immutable and a mutable slice at the same time — this would violate the borrowing rules.