Boxing things in Rust
Image provided courtesy of Pexels, PixaBay

Boxing things in Rust

Rust's memory management can feel a bit intimidating for most developers. Rust gives you some control over whether you're allocating data on the stack or the heap and its up to you to decide where memory should be allocated.

When you allocate memory for values in Rust, that memory is allocated on the Stack. Memory on the Stack is allocated in a "stack frame" which contains information for the Stack's local variables and some metadata about that stack frame. When a function exits, that functions stack frame gets deallocated. This structure is a LIFO (Last in, First out) pattern for reclaiming memory.

The stack does not work for every use-case. For example, you may want to pass some memory between different functions or keep some memory alive longer than a single function's stack frame. To do this, you can allocate memory on the heap (sometimes referred to as dynamic memory).

Stack? Heap? Whaaa?

If the idea of the Stack and/or Heap is confusing -- no worries! Memory management is a very complicated topic but it's fundamental when reasoning about how computers operate.

Most programming languages help abstract these concerns so you don't have to worry about them, however there are a few that do not. Rust is one of those languages. If you don't feel comfortable with the Stack and Heap yet, you can learn more about them as you go! Don't let it hold you up!

Boxing is a practice for allocating memory on the heap. A Box is a smart pointer to a value allocated on the heap. As an example, if we wanted to box a User, we can use a Box<User>.

Boxes are created by using Box::new, and Box::new takes in the value that you'd like to box. In some cases, you may have to provide a generic type to the box to help it identify what type of values the box is containing.

Boxes implement the Deref trait so you can work with boxes as if you were working with the values that the box contains. To clarify, if you Box a User struct, you can access the fields of that User struct without manually needing to deference the User from the box.

This example is a bit contrived, but hopefully it showcases how boxes automatically deref when needed:

pub struct User {
    name: String,
}

fn main() {
    // b here is a Box<User> NOT a User
    let b = Box::new(User{name: String::from("Brad")});

    // deref kicks in here
    //so we can treat b as a User instead of a Box<User>
    println!("{}", b.name);
}
        

In this example, our User gets stored on the heap and b, on the stack, is the pointer to the user on the heap.

One thing that's really neat is that boxes do not have additional performance overhead besides the impact of storing data on the heap instead of the stack.

I've mentioned that Boxes are "Smart Pointers" but what exactly is a Smart Pointer? Smart Pointers are usually implemented via Structs but they also have to implement Deref and Drop. Deref was mentioned above, but we haven't mentioned Drop yet. The Drop trait allows you to run code when the struct goes out of scope, in the case of smart pointers, deallocation. There are other smart pointers besides Boxes, but those are out of the scope for this article. Perhaps another time!

This post was originally written for BradCypert.com but has been reformatted for your reading pleasure on LinkedIn.

要查看或添加评论,请登录

Brad Cypert的更多文章

  • Modern times call for modern languages

    Modern times call for modern languages

    Computing has only been around for roughly 70 years. While that may seem like a long time, it's actually an extremely…

  • Communicating in Code

    Communicating in Code

    Writing elegant code is an extremely difficult task. In some sense, you can think of it as writing a paper in a mix of…

    1 条评论
  • Dart all the way down

    Dart all the way down

    If you're using an app and it's built with a cross-platform framework, chances are high that it's either built with…

    1 条评论
  • Type Systems

    Type Systems

    This issue is going to deviate a tiny bit from the more theoretical format. Instead, we're going to dig into why I…

  • The one on burnout

    The one on burnout

    I really, really enjoy writing code. It's both my hobby and my career, and in that sense I am extremely fortunate to be…

  • How to write less code without losing your mind

    How to write less code without losing your mind

    Today's issue is near and dear to my heart. As I've moved up in my career (in the "away from code" sense, not the…

  • Shortening the SDLC

    Shortening the SDLC

    One thing that has been on my mind a lot, recently, is how the SDLC is effected by modern development practices like…

    4 条评论
  • Read Only Memories: Optimizing Software

    Read Only Memories: Optimizing Software

    Hi! I wanted to give you some insight into what exactly Read Only Memories is. The goal with Read Only Memories is to…

    4 条评论
  • Understanding Clojure's map and pmap

    Understanding Clojure's map and pmap

    Let's be honest - Part of the reason you're using Clojure is the higher order functions like . They're great…

  • Introduction to Asynchronous Processes in Clojure

    Introduction to Asynchronous Processes in Clojure

    I've been writing about Clojure for a while now, and after some requests, decided to write about Core.Async.

社区洞察

其他会员也浏览了