 Cool. So my name is Matt Cox. I'm at Customer I.O. Most of my day is spent head down in Ruby and before that JavaScript and before that I didn't really code. It's becoming more and more common now for people to learn how to code through just Ruby, Python, JavaScript and a whole host of other languages because it's fast enough and it's simple enough that you can get in and start really working on the fundamentals of development. Let's see. The thing is you can't really take the comp side background for granted anymore. So a large amount of Rust stations now are C and C++ programmers who have been burnt. They know the pain of null pointers, uninitialized memory, data races. But there's a whole host of other people who are largely much quieter mostly because you don't walk into a room of people who know everything and shout, hey I know nothing. Maybe we should. Right? Yeah. But these high level language users have their own problems. They have to basically appease the interpreter or compiler in some cases for long running GC cycles. They have to try to optimize with basically not a full set of tools and they're not given a chance in basic things like memory allocation to the point where you don't really have to know much about them because everything is handled for you. So this talk is just going to be a basic overview of basically memory management and data representation in Rust. It should be a good overview for people who don't know any of it so that they can jump into existing literature and not feel scared. And then for people who haven't worked with it in a while, hopefully it'll be a nice fun way to refresh too. So let's start extremely simple. This is about as simple as it gets, maybe one plus variable or just an empty main. But we have a main function, declare variable foo inside it, set it to one and then set the bar variable to three. But it's what's underneath that makes this interesting. When main and any other function really gets called, we allocate all of the local variables because Rust knows the types of foo and bar here, we know how much memory they take up so we can go ahead and allocate the correct amount of memory. It then assigns the value one to the first slot in the allocated memory. And then it goes ahead and sets bar on top of foo. Basically just filling that out. Good ol' Ferris doing the hard work there. So when it reaches the end of the program, we're done with it and the memory is deallocated. Then it reaches the end of the program and yay! So what Ferris just built is called the stack. It's a fairly basic data structure and is a good way to think about memory on the low end. You see memory for each local variable is allocated at the very beginning of the function. Some caveats but for that. And then because we know the size of the type, we know how much to allocate and it's the allocated at the end and because it just goes in sequential order, it's very fast. So we're going to go ahead and make things a little more complicated. So let's see. We're going to go ahead and start in the main function and we're going to allocate just enough memory for all the local variables in the stack. And then we're going to move all the values into those memory slots. So now we're resting on second funk and when we call it, it works just the same way. We allocate all of the memory for the local variables, go ahead and fill them out and then go all the way down to the third funk. And again, same way. So now notice that I've color coded all of the memory based on what functions allocated it. Each of those are called a stack frame. In reality, they hold a little more information than just the variables, but you don't really have to worry about that right now. So now that we've reached the end of the third funk, it means it's time to deallocate the entire stack frame. Everything that was allocated in that, any kind of stack allocation inside that function was then deallocated. And then move up to back to the second funk. And because we stacked the third funk on top of it, as soon as we remove it, we're now back in the stack frame of the second funk. And so that's how we get all of these stack traces, right? All the frames on top of each other. This is called last in first out, or LIFO, or LIFO depending on how you pronounce acronyms. And in the same way, move up, then we go to main, all the way down, and we're done. So why can't we just use the stack for everything? First of all, the stack has a limited size. So if we need something massive, we're not going to be able to do what we need to do. Plus memory on the stack only lives as long as the function, which is why we get all these fun lifetime errors that we need to debug, fight the compiler. And if we need to pass that memory to other functions, then it's not going to be very useful. Memory on the stack also requires us to know its size. The ones earlier we know that it's, say, I32 or something like that. So we know how much memory to allocate. But if we didn't know that beforehand, say we're asking the user for input or we're reading from a file or something where we can't just say this is how large it is, we're going to need something different. Same as if we need to change that size at runtime like a VEC or a string. So then we have the heap, the nice little counterpart. It's only, in theory, limited by the amount of memory in your machine or until your OS goes, hey, no, this is a bad idea and kills it. It's going to be accessed outside of its call frame and can stay alive past the function that allocated it. It does not have to be contiguous. What that means is we don't have to know the size of the data beforehand, but it's also not very neatly stacked. So we have the stack starting at sort of the beginning of your computer's memory. It's kind of easy to think of this as an array. And then when we allocate on the heap, we start at the top, the green box there and go down. But it's not always contiguous. It's not just a stack on the other end. We can basically just go through and it'll find something that's big enough for us to allocate in. So if we have a massive thing, we're going to say we have a VEC with a few million things inside of it. You can't just place it anywhere because you're going to overwrite other memory. So it goes through and it finds where open place is. So one thing I haven't really mentioned yet, and we can't really talk about heaps without, is that all of the memory is addressed. You'll often see them in like hex, just different radix. But more or less, you can just think of the huge number at the top as the maximum amount of memory that's given to your program or is in the system depending on how you're running your program. We're just going to refer to them by normal numbers because it's way easier. So back to a super simple thing. In Rust, we allocate on the heap by using the box type. And you literally say box new and then throw in the value. So we've got all of this unused memory, nice blank canvas. Just like before we enter main and we allocate the stack. And since the box has a defined size, we know how to treat it just like we would any other variable, right? The things that we can store things of basically any size inside of box and what we allocate in the stack is going to be the same. So just like before we move the or copy over the local variables into the stack. But now what does fares do with bar? It has a lot of work ahead of it. Since we've used the box type, we know it will be heap allocated and it checks the size of the value. Then goes to the available memory and finds an empty space large enough for the value to be stored. So once it finds a space big enough, it goes ahead and allocates it. Then it will store the value there, in this case three. Then it will take the address of that memory and store it in the stack under the label bar. So we haven't really talked about pointers yet. But that label, basically the index, is a reference to memory located elsewhere. When people talk about dereferencing a pointer, all that means is that we have an address and we're going up and we're finding the value that's stored in that address. So in Rust, pointers are signified with the amber sand symbol. It's actually pretty amazing how easy it is to write Rust without actually knowing what that is other than just the word pointer. Which is huge compliments to the compiler team making that nice and easy. So now we're at the end of the program and we go ahead and deallocate the memory. Just like the other one. Got nice blank canvas again so we can start it up. So the heap takes a lot of work. You hear about people avoiding heap allocation? That's because it's got to figure out the size of the data. It's got to find an area large enough to store it. It's got to store the address in the stack and have to do reference it whenever we use it. It's quite a bit of work compared to just store this here. And that allows us to use things like vec and string since we only ever really know the size at runtime. This allows us to be way more flexible with lifetimes because it can live past the originating function and be referenced from multiple places. So Rust is stack allocated by default. It's very clear when you're allocating on the heap, just box or anything that uses box. We get a considerable performance boost from this compared to say Ruby and Python which are heap allocated by default. There are some fun little optimizations that they do to make that not always 100% true. But in general when you create something in Ruby or Python it's going to be allocated on the heap. Unless you FFI in some Rust, it's going to it's just sort of the way it's going to work and you don't have much choice. Just back to the simple thing. These are roughly equivalent. And there's no and already this seems sort of weird when you see how much work the heap is doing. Though it's not 100% the same since Ruby is a garbage collected language. We're seeing you're talking about allocating memory, managing ourselves. But garbage collection is basically just a way that languages that use it, deallocate memory without us having to think about it. And it's kind of nice, especially when you do things like build graphs or like really deeply nested data structures because you don't have to think about it. But it also comes with a massive performance. So Rust doesn't have garbage collection. But we have something kind of sort of like it, sort of GC lite called RC. RC stands for reference counting. So while I stall, I forgot why I have an old version of this. Does anyone have anything that I messed up on? Ooh, already. Yeah. Cool. For those of you listening at home. What he was saying is that depending on the use case and the type of GC allocation is extremely cheap sometimes and it's can actually be faster in some cases. The thing that I want to point out is just that it's nice to have the choice. But yeah. So this is a little embarrassing. But I should have used to get it looks like. Yeah. So I'm gonna try to save this as best I can. And just play it off. But anyway, I guess that's an intro to the memory management. And then let's see. So for the stuff that I didn't talk about, you can go through and Steve Klobnik actually has a fantastic thing on the stack of the heat in the Rust book. Thanks Steve for all of that and all of the documentation stuff that you've done. And then the Gankros recently released leaked advanced Rust book actually was just happened and it has a lot of the data representation stuff that isn't here. What is it? Not Macon. Nice. And then Mike Pickle's blog he's doing the Rusty Rubius posts which are extremely nice. And then as always the Rust standard library is fantastic for weird tricks especially any type of collection. Again, didn't get in the unsafe code. But through unsafe code on unsafe on code and break things. It's really hard to figure out and appreciate the safety net. When you can't see the ground you haven't crashed a few times. Obviously not recommended for production. Just learning. And see how far you can go without the standard library. It's not easy but it's really fun. Yeah. Yeah. Just all I can ask and I'll get I'll find the real slides and get them up on Reddit hopefully. But just really hope that if you ever come across someone trying to learn Rust who doesn't know any of this stuff. It's really hard to jump in without that kind of background. And it's a really fantastic thing that people are trying to learn. Help them. Write things on just things that people take for granted. And if you write even something fairly complicated go ahead and broaden your audience a bit. Make sure that you include all the details and not just assume knowledge. And get Rust.