Close Menu
    DevStackTipsDevStackTips
    • Home
    • News & Updates
      1. Tech & Work
      2. View All

      Microsoft donates DocumentDB to the Linux Foundation

      August 25, 2025

      A Week In The Life Of An AI-Augmented Designer

      August 22, 2025

      This week in AI updates: Gemini Code Assist Agent Mode, GitHub’s Agents panel, and more (August 22, 2025)

      August 22, 2025

      Microsoft adds Copilot-powered debugging features for .NET in Visual Studio

      August 21, 2025

      ChatGPT is reportedly scraping Google Search data to answer your questions – here’s how

      August 26, 2025

      The 10 best early Labor Day deals live now: Save on Apple, Samsung and more

      August 26, 2025

      5 rumored Apple iPhone Fold features that have me excited (and frustrated at the same time)

      August 26, 2025

      Forget plug-and-play AI: Here’s what successful AI projects do differently

      August 26, 2025
    • Development
      1. Algorithms & Data Structures
      2. Artificial Intelligence
      3. Back-End Development
      4. Databases
      5. Front-End Development
      6. Libraries & Frameworks
      7. Machine Learning
      8. Security
      9. Software Engineering
      10. Tools & IDEs
      11. Web Design
      12. Web Development
      13. Web Security
      14. Programming Languages
        • PHP
        • JavaScript
      Featured

      Log Outgoing HTTP Requests with the Laravel Spy Package

      August 26, 2025
      Recent

      Log Outgoing HTTP Requests with the Laravel Spy Package

      August 26, 2025

      devdojo/auth

      August 26, 2025

      Rust Slices: Cutting Into References the Safe Way

      August 26, 2025
    • Operating Systems
      1. Windows
      2. Linux
      3. macOS
      Featured

      Best AI Girlfriend Simulator [2025 Working Apps and Websites]

      August 25, 2025
      Recent

      Best AI Girlfriend Simulator [2025 Working Apps and Websites]

      August 25, 2025

      8 Best Paid and Free AI Sexting Chat Apps in 2025

      August 25, 2025

      Best AI Anime Art Generator: 7 Best to Use [Free & Premium]

      August 25, 2025
    • Learning Resources
      • Books
      • Cheatsheets
      • Tutorials & Guides
    Home»Development»Rust Slices: Cutting Into References the Safe Way

    Rust Slices: Cutting Into References the Safe Way

    August 26, 2025

    What is a slice?

    It’s just a reference, but of course that is being over simplistic.

    Let’s delve deeper into what a slice type is.

    According to the official Rust documentation and cookbook,

    A slice can be defined as:

    • Slices let you reference a contiguous sequence of elements in a collection. A slice is a kind of reference, so it does not have ownership.

    One notable word to really understand what a slice is the word collection. So what is a collection?

    According to the standard library documentation:

    • Rust’s standard library includes a number of very useful data structures called collections. Most other data types represent one specific value, but collections can contain multiple values. Unlike the built-in array and tuple types, the data that these collections point to is stored on the heap, which means the amount of data does not need to be known at compile time and can grow or shrink as the program runs.

    In simple terms, it is a well-articulated list of most-used heap-based data structures. Collections are of different variants and groups but they all have one thing in common, and what is that?

    All collections store data on the heap, allowing them to be easily extensible and making compile-time size allocation easy as collection types only keep key information on the stack, like size.

    Now we have that out of the way, you can easily define a slice type. If you asked me, I would say it is simply a ranged reference to contiguous data. Because slices themselves are stored as a pointer and length (on the stack if local), the core data itself could be an array (on the stack), a Vec (on the heap), a string, or other contiguous memory.

    Why and when should we use a slice?

    Now we know what it is, let’s explore when to use and why we could need a slice.

    Slices are mostly about viewing data without taking ownership. You don’t always need to copy or move things around just to work with them. Instead, a slice gives you a window into the data, so you can read or mutate part of it while keeping the original owner intact.

    They shine in scenarios like:

    • Working with substrings or sub-arrays: Instead of creating a new string or array for a subset, you just borrow a slice of what you need.

    • Avoiding unnecessary copies: Since slices are lightweight references, you don’t pay extra performance costs for moving or cloning data.

    • Function parameters: Instead of writing separate functions for String and &str, or Vec<T> and &[T], you can just take a slice parameter and handle both cases with the same code.

    • Iteration with safety: Slices enforce boundaries — you won’t run past the edge of your data. This is both memory-safe and avoids panics when used with safe methods.

    So in short, use slices whenever you only need to look at or operate on part of a data structure without owning or duplicating it. They’re a way of being precise with memory and scope while still staying flexible in how your functions and APIs accept input.

    Slice of Strings.

    Since strings are a good example of contiguous memory , lets take it as an example of how to make a slice of a string and with this example you can learn the basics of working with slices ; we would of course then go into deeper categories like making slices of other collection groups.

    Let’s look at this example. In this example, we would create a string, take a slice of the string and pass it to a macro called println! to print it to the standard output. 

    
    fn main() {
        let s1 : String = String::from("Hello world!");
        let s1slice = &s1[..5];
        println!("{s1slice}");
    } 

    In the above example you can see we can create a slice of a string by simply using the & ref and passing in a range, so in code a slice looks more like a reference with a range.

    Safety with Slices

    Indexing and panics:  Even though our previous example is totally ok , it is prone to runtime errors, why? If the given range is bigger than the size of the collection in respect, there would be a runtime error. 

    In order to fix this, we can ensure we supply correct ranges or simply use helper methods that help avoid this entirely, as they would return an option type of Some(slice) or None when the range is too big, or any other possible issues arise.

    Here is an example of our previous function, but with a safer alternative using the .get method.

    
    fn main() {
        let s1 : String = String::from("Hello world!");
        let s1slice = s1.get(0..5).unwrap_or("Hello");
        // I use unwrap_or as a fall back, incases of a none.
        println!("{s1slice}");
    } 

    The way this works may differ slightly across collections, but the principle is the same: direct indexing can panic, so prefer safe methods when possible.

    • Note: Since String is essentially a Vec<u8> with UTF-8 rules, slicing at a non-UTF-8 boundary also causes a panic.

    Mutability Rules

    Yes, you can have mutable slices. As mentioned earlier, slices are references with a range, so you can also take a mutable slice (&mut [T] or &mut str). However, the borrow checker applies the usual rules: you can have either one mutable slice or many immutable slices at the same time, but not both. This ensures safety when modifying data through slices.

    Here is an example showing this:

    
    fn main() {
        let mut numbers = [1, 2, 3, 4, 5];
        let slice = &mut numbers[1..4]; // mutable slice of part of the array
    
        for n in slice.iter_mut() {
            *n *= 2;
        }
    
        println!("{:?}", numbers); // [1, 4, 6, 8, 5]
    }

    Lifetime & Ownership

    Even though slices reference owned data, they themselves do not own it. A slice is just a fat pointer (a reference plus a length). For example, &[u8] means “a reference to a sequence of u8s.” 

    The underlying elements are not duplicated — the slice just points to them.

    This means:

    • If you slice a vector of values, you get a slice of those values.

    • If you slice a vector of references, you get a slice of references.

    Let’s look at some examples that expose this:

    
    fn main() {
        let v1: Vec<i32> = vec![1, 2, 3, 4];
        let v2: Vec<&i32> = vec![&1, &2, &3, &4];
    
        let slicev1: Option<&[i32]> = v1.get(1..3);
        let slicev2: Option<&[&i32]> = v2.get(1..3);
    
        println!("{:?}", slicev1); // Some([2, 3])
        println!("{:?}", slicev2); // Some([&2, &3])
    }

    Notice the type signatures:

    • slicev1 has type Option<&[i32]>, meaning a slice of i32s.

    • slicev2 has type Option<&[&i32]>, meaning a slice of references to i32s.

    This shows that slices preserve the nature of what they point to — values stay values, and references stay references. 

    So the core ownership rules for references are still in effect, and just like ownership rules lifetime rules are stay the same — slices cannot live longer than their owners the compiler enforces this.

    Borrow Checker

    Just like other points related to references, you can’t bend the borrow checking rules around slices either. 

    The same restrictions apply: you can have either many immutable borrows or one mutable borrow at a time, but never both. Attempting to break this rule results in a compiler error, not a runtime panic.

    For example, if you try to mutate a slice while still holding an immutable reference to it, the borrow checker will reject it at compile time. 

    Likewise, if you attempt to create two overlapping mutable slices of the same data, Rust prevents it. 

    These rules ensure that slices are always safe to use without data races or undefined behavior.

    Safe Iteration

    Instead of direct indexing of slices, you might want to use iterator methods such as .iter() and .iter_mut() when you want to read or modify elements in sequence. Iterators avoid the risk of indexing mistakes and make the code more expressive. In cases where you need ownership of the elements, .into_iter() is also available.

    Additionally, when working with ranges, the .get() method is a good choice because it returns an Option and avoids panics if the range is invalid. This is especially useful for dynamic scenarios where you cannot guarantee the bounds at compile time.

    Limitations

    As seen so far, slices and their usages are tied to contiguous memory data structures such as arrays, vectors, and strings. They provide a safe view into a block of sequential elements. However, not all collections in Rust are contiguous — for example, HashMap, HashSet, and LinkedList are not laid out sequentially in memory.

    For these collections, you cannot take a slice directly. Instead, you rely on their own iterator methods or conversion functions to access data safely. If you require slice-like functionality, you would often need to first collect the elements into a contiguous container like a Vec.

    Next Steps

    In the next section, we will show some examples of getting slices out of popular collections, highlighting how they behave with vectors, arrays, and strings.

    The Four Groups of Collections and How to Take Slices of Them

    Rust’s collections can be grouped into four major categories. Out of these, only the contiguous-memory collections support slicing directly. The others provide access through iteration or conversion. Let’s walk through them one by one.

    1. Sequence Collections

    These are contiguous-memory collections such as arrays, vectors, and strings. They support slicing directly.

    
    fn main() {
        // Array slice
        let arr = [10, 20, 30, 40, 50];
        let arr_slice: &[i32] = &arr[1..4];
        println!("{:?}", arr_slice); // [20, 30, 40]
    
        // Vector slice
        let v = vec![1, 2, 3, 4, 5];
        let v_slice: &[i32] = &v[0..3];
        println!("{:?}", v_slice); // [1, 2, 3]
    
        // String slice
        let s = String::from("Hello, world!");
        let s_slice: &str = &s[0..5];
        println!("{}", s_slice); // "Hello"
    }

    2. Map Collections

    Examples: HashMap<K, V>, BTreeMap<K, V>.

    These are not contiguous, so you cannot slice them directly. Instead, you use iterators or collect into a Vec to slice.

    use std::collections::HashMap;
    
    fn main() {
        let mut map = HashMap::new();
        map.insert("a", 1);
        map.insert("b", 2);
        map.insert("c", 3);
    
        // Collect into a Vec for slicing
        let mut entries: Vec<_> = map.iter().collect();
        entries.sort_by_key(|(k, _)| *k); // ensure order before slicing
        let slice = &entries[0..2];
        println!("{:?}", slice); // e.g. [("a", 1), ("b", 2)]
    }

    3. Set Collections

    Examples: HashSet<T>, BTreeSet<T>.

    Like maps, sets are not contiguous, but you can convert them to a Vec and then slice.

    
    use std::collections::HashSet;
    
    fn main() {
        let set: HashSet<i32> = [1, 2, 3, 4, 5].iter().cloned().collect();
    
        // Collect into Vec for slicing
        let mut values: Vec<_> = set.iter().collect();
        values.sort(); // ensure stable order
        let slice = &values[1..4];
        println!("{:?}", slice);
    }

    4. Linked Collections

    Examples: LinkedList<T>.

    Linked structures are inherently non-contiguous, so slicing is not supported. Instead, you rely on iteration or manual traversal.

    
    use std::collections::LinkedList;
    
    fn main() {
        let mut list: LinkedList<i32> = LinkedList::new();
        list.push_back(10);
        list.push_back(20);
        list.push_back(30);
    
        // Collect into Vec for slicing
        let values: Vec<_> = list.iter().collect();
        let slice = &values[0..2];
        println!("{:?}", slice); // [&10, &20]
    }

    Summary

    • Sequences (arrays, Vec, String): Direct slicing supported.

    • Maps, Sets, Linked structures: No direct slicing. Must iterate or collect into a Vec first, then slice.

    As you can see, this shows how slicing is tightly connected to contiguous memory layouts, which is why only certain groups of collections support it directly — in other words, you would have to convert those data types first before slicing.

    Now you know when to use slices and how to use them, and what they really are; feel free to make those modifications to your algorithms and function signatures. 

    If you have any questions , let me know if you have any questions. 

    Source: Read More 

    Facebook Twitter Reddit Email Copy Link
    Previous ArticleProvisioning a Cloud Project for Optimizely Configured Commerce
    Next Article How to Build an Advice Generator Chrome Extension with Manifest V3

    Related Posts

    Development

    Log Outgoing HTTP Requests with the Laravel Spy Package

    August 26, 2025
    Development

    devdojo/auth

    August 26, 2025
    Leave A Reply Cancel Reply

    For security, use of Google's reCAPTCHA service is required which is subject to the Google Privacy Policy and Terms of Use.

    Continue Reading

    Pro-Palestine activists protest at Microsoft’s site in the Netherlands over Israeli military data

    Operating Systems

    CVE-2025-0856 – WordPress PGS Core Plugin Unauthenticated Remote Data Manipulation

    Common Vulnerabilities and Exposures (CVEs)

    QuickPoint

    Web Development

    Millions of Node.js Apps at Risk Due to Critical Multer Vulnerabilities

    Development

    Highlights

    CVE-2025-7436 – Campcodes Online Recruitment Management System SQL Injection Vulnerability

    July 11, 2025

    CVE ID : CVE-2025-7436

    Published : July 11, 2025, 4:15 a.m. | 5 hours, 22 minutes ago

    Description : A vulnerability was found in Campcodes Online Recruitment Management System 1.0. It has been declared as critical. This vulnerability affects unknown code of the file /admin/ajax.php?action=delete_vacancy. The manipulation of the argument ID leads to sql injection. The attack can be initiated remotely. The exploit has been disclosed to the public and may be used.

    Severity: 7.3 | HIGH

    Visit the link for more details, such as CVSS details, affected products, timeline, and more…

    CVE-2025-4984 – City Discover City Referential Manager Stored XSS

    May 30, 2025

    CVE-2025-52926 – Spytrap-ADB Stalkerware Detection UI Vulnerability

    June 23, 2025

    CVE-2025-8043 – Firefox URL Truncation Vulnerability

    July 22, 2025
    © DevStackTips 2025. All rights reserved.
    • Contact
    • Privacy Policy

    Type above and press Enter to search. Press Esc to cancel.