 This is our next speaker. His name is Zeshan, correct? Zeshan. Zeshan, it's so hard today. He's going to talk about the last memory management, and dives, and memory safety. I guess you can start. You can do yourself, and just go. Thank you. Hello, everyone. I wasn't expecting this many people. Say, there are four people because it's 10 in the morning. But yeah, thanks for coming. My name is Zeshan. I work at a small company in Berlin called Kinfolk. It's a far higher Linux expertise company, mostly work around Kubernetes and stuff, container technologies. And I've been fond of Rust for a long time, ever since it was first introduced. It was pretty unstable, so nobody could use it. But lately I've been using it, especially now also at work a bit. So yeah, I wanted to introduce memory management because a lot of people when they come across this new language, they stumble with it, at least I did. I was really having a hard time with the memory aspect of it. So it's good to have an introduction to that. So how many of you know a sensual Rust like some of it? Okay, quite many. So I won't get into a lot of details anyway, but I'll just quickly introduce the aspects that I'll use later in the examples. Well, mainly it's like a system programming language. So some programming languages, they ensure safety. Some programming languages ensure efficiency. But with Rust, you have both of them at the same time. There's the focus on both of these things in the design of the language. So it makes it a really awesome language, in my opinion. And you have a concept of zero cost abstractions. So everything you code in Rust, it will have equivalent performance as if you write it in C or C++. Actually, Sebastian, he's in the queue outside, I think. He's not here, right? He's working on GStreamer. He's writing GStreamer plugins and elements in Rust. And he actually saved a lot of CPU. He made the code perform much better, because in Rust you can achieve it more easily because of the zero cost abstractions that we have in Rust. It has a non-mutable state by default. So if you want to mark something as mutable, you have to use a keyword. So when you have a problem in your code base, so you can know which variables and which parameters to look for, because if it's a non-mutable state, it won't give you any problem, because it doesn't change. So why would it create a problem? So it's a mutable state that you need to look for, and that's why by default you have everything non-mutable. And it has strict ownership semantics. In C and C++, you usually just give a pointer to someone, and then you have to establish some sort of way to make sure that nobody double-freezes it, or especially in C, we have been using in GNOME glib libraries for reference counting all objects, so you have to handle that manually in C, of course. But in Rust, you don't have to do that manually, but you have to think about how ownership works, and at one time there can be only one owner to a resource. I'll show you the examples later how that is. So we will start with a simple example. In other programming languages, something like this, you're passing an array vector to a function and it's just adding first two elements of that and returning it. And in many languages, this would just work fine, but you add that and then you show it, it's fine. But in Rust, this won't work because when you, as it says, when you passed the vector there, you passed the ownership to that function, and you never got that ownership back. So later on, you can't use the resource that you already passed to it because you gave it the ownership. And the main thing is there are two kinds of types in Rust. There's copy types and then there's move types. By copy types, there is the simple types in Rust, for example, integers and booleans and those kinds that can be passed around like as copies without any performance problems, easily copied. Those always get copied. So when you pass it to a function, if in the example, if I was passing integers directly, that example would have worked because it's a copy type. But move types, which are most of the types by default, if you create your own data type, it will be by default a move type. And by move, I don't mean the whole thing moving, but moving of ownership. So you give the ownership to someone else when you pass it to them, if you pass by value and you can't use it anymore. And that's, as I said, in Rust, by default, everything has only one, every resource has only one owner at a time in the code. So it's very clear and it's, then Rust so knows when to free the resource and not to. But the thing is you can't work like that. You have to have multiple owners and stuff. And one of the first ways to do it and one of the most obvious and most used is borrowing, which is like in C++ and C++ by reference, more like C++. So the same example, we just turned the parameter into a reference. So when you pass it by reference, you are borrowing the ownership of that resource to that function you're calling temporarily. And once that scope in which it's borrowed, that's over, then the borrowing is finished and you have your resource back. But if I had kept ownership, for example, if I had set m%v and then assign it to a local variable, it has the same scope as v then and then I could not have used it while it's still borrowed. So while it's borrowed, you cannot use it, but once the borrowing is finished, which is based on scopes and the scope in this case is that function that you called. So when it returns, you've got the ownership back. I went following so far or I have 20 minutes to explain this very complicated subject, so I'm trying my best. But the problem it borrows is, as I said, it's temporary. You get it back and the resource that borrowed it, you can't keep it forever and you don't have the ownership at the same time exactly. So we will start with an example. I fly helicopters, so my examples are related to that. So you have a registration, every aircraft has a registration, so it's a simple struct. And I have an implementation for it. It's just a constructor. Get a registration as a string. This string type is an owned string type, so there's two string types in Rust. One is the owned, one is not owned. The non-owned is a borrow. It's a piece of the string, which could be the whole string itself as well, but it's a borrow. But this string with the capital S is owned type. So from main, we pass it a string, an owned string, and then we want to just use it there. And then after we pass it to the Heli, the constructor, we again use it ourselves. And that won't work because when we pass by value, we gave it the ownership. So as you like last time saw, it's very similar, or actually it's the same, just different context. You move the value and you can't use it anymore. So what would we do in this case? The easiest solution is to use a data type called RC, which is abbreviation for reference counting. And what it does, it's a container type, which you put something in it and it adds reference counting to that resource. So the resource itself, you don't need to copy it around if you want to pass it around and have multiple owners in the same code of the same resource. And instead you just use RC. And what it does is like each time you create a new user for it, you increase the reference count on that resource, contain resource, and then once you are, all these scopes are finished that have borrowed, that have increased the reference count, rust automatically destroys the resource, the underlying resource, because now there is no owners for it. So it's just like any reference counting in any programming languages you must have seen. So in this case, we do exactly the same, but instead of passing the string directly, we now put it in a RC and pass the RC's clone method. So by clone, I created a new reference count, I increased the reference count on it. So when it goes to that one, then when the new function, it decaps the ownership. So now we have two owners of the same resource. So when I said in the beginning that you can only have one owner at a time, I kind of lied, but not exactly because you really can't have two owners, but Rust provides you ways of working around it in a safe way. In other programming languages, you can just really nearly pass things around and ownership, there can be multiple owners. And the language have no way of testing if you will destroy the resource while another part of the code is still using that resource. But in Rust, since you put it in RC, Rust ensures that the resource is always there as long as there's a user of that resource. Sir? No, it's not, it's a, yeah, in this case, it just increases the reference count. Good question. But the problem is RCT is not for multiple threads. If I run this, for example, now I introduce, it's the same example as previously, almost except this time I have added a method and I'm calling that method from another thread. So I launch another thread to call that method and now multiple threads are using the same RC, which we have RC in the struct. So this one will end up in an error, something like this. Rust have these traits, concept of traits and different structs implement different traits and that's how it ensures that different guarantees are met. And in this case, there's a trait called send that needs to be implemented by structs if they want to be accessible by different threads. So we need a different struct, which implements that trait that Rust needs if you want to access it from different threads. And that struct we have in the SDD, in the standard library is Arc. It's just atomic reference counting. So the reference count is atomic in this case. It's very similar to the RC. The main thing is that it's thread safe. So instead of RC, I use Arc. And the same example, it's same code will just work. Yeah. But the problem with then RC is that it's not, sorry, Arc is that it's not mutable. So you can't like get a resource out of it and then modify it. That's not possible. So let's look at example. So this time, it's the same exact code again, except that in the hover method, now we are modifying the resource. And the methods I'm calling is just the normal methods of the string type, clearing the string and then pushing another string on the existing string. But this won't work because I'm modifying it. And the main thing is I can't borrow it as mutable because it's not a mutable resource. I didn't mark it as one either. So for those things, you need yet another data structure called mutics, which is just like a mutics in any programming languages and libraries. You get locks on the resource. So we put arc, mutics in an arc because we want to access it from multiple threads atomic with atomic reference counting. So the arc is adding atomic reference counting and then the mutics is ensuring that you can have mutable locks on the resource. So you have multiple container types. You will see that in sometimes in Rust, you need to have multiple containers to the same resource. But the thing is you have type aliasing. So you can use that to create a new type for your complex type very, very easily. That's just alias. So it makes things pretty easy to follow. Anyway, so this time you just use mutics. You create like when you created the arc, you in there you just do a new mutics and put the resource in the mutics. But in the hover, you take a lock of the, from the mutics. And when you access the mutics, it's since it's contained in arc, you get an atomic reference. So you, it's a thread safe. And once you get the lock, you then you can do whatever you want because you have gotten yourself a mutable reference to the resource. And then the thing is that Rust is very much, the memory management is very much based on scopes. So once you are out of that scope, which is the hover method, the lock that you acquired will be freed automatically for you. So you don't have to free it. If you want to, as far as I know, there is an unlock method, but usually you don't need to, you just use scopes to signal like that. Okay, I'm done with the lock. And similar to the mutics, there is a type called read write lock, which is if you have multiple threads that some of them needs only read access, not write access. So it could be very inefficient if you have, if you use mutics because every thread will have to wait for the other thread to finish access and then be able to access. But the read write lock, you can have multiple readers of the same resource. So multiple threads can lock on the same resource at the same time as a read read lock, but not write lock. So, but if there's a thread having a write lock, then others have to wait for that to finish. Yeah, it's very similar. I'm really sorry I'm going really fast, but I have very little time as I mentioned. So box is a type when you usually create a resource, you put it usually on the stack. And, but if you use those data structures that I mentioned, RCE, ARC and those, you put it on the heap. But it's more heavy. RCE has a reference counting and it needs to ensure that reference count increases, decreases, blah, blah, blah. So you don't want to use it just for allocating on the heap. So if you want to just allocate something on the heap to pass it around and keep the resource around for longer than one function or something, you can use something called box. And it's very simple, you just create the new box. And the five in there is not the size. People think of it as size, but that's just an integer that I want to keep in the box. And then you just use it as if it's not contained in a box. It's usable as if it was just the integer itself, just like in our case of RC and ARC. Another thing with the box is very important is that it gives the resource it keeps a size. So if, for example, this case, in Rust, your enums can be complex. You can have data inside your enum, particular enums. So in this case, we want to create a list that have other nodes in it. So we want to keep a list in a list. And we can't do that because Rust won't be able to tell how much memory to allocate to this because it's infinite recursion. We can break that infinite recursion by using box because box, the list, if it's going to be a list, it's on the heap and you only get a pointer in the box. And pointer is always the same size, so you solve the problem without using any pointers itself. In Rust, you can use pointers, especially an unsafe code when your market is unsafe. But typically, you can avoid it and you should avoid it because that's where the problems are usually. Yeah, I wanted to discuss briefly the lifetimes. And that was another reason I was hurrying up. I have done the same talk before in other conferences, but I always avoid lifetimes. So this is my first time addressing it. I hope I can do some justice to it because people are afraid of it. So as I mentioned, you can pass things by reference. And references are temporary. And the scopes define the lifetimes of things. So in this case, you get two strings in the longest function. It just returns the longest string you have. I'm sorry, I have my pointer here. And so this should work, right? Because you just pass the references, you get the reference to the longest string. And yeah. But it wouldn't work because Rust doesn't know when. And you get very helpful messages nowadays from Rust compiler, so that's really cool. So you borrowed a value and then you return a reference to one of the borrowed values. And Rust doesn't know, well, he can easily can't easily tell which one it is. And you have multiple in parameters and one out parameter. So in this case, you have to tell it how long would the reference that you're returning have to be kept alive. And what other resources it is associated with. So it keeps them both alive long enough. So we can, I know the syntax of lifetimes is a bit, takes a while to get used to. And even when you get used to, you still hate it. But that's how it is. I have no better suggestions, so I don't criticize much. But I know that it seems really, really weird. So you declare in the function definition a lifetime. It's an abstract concept of the lifetime. And you declared that all the resources that you're using, like the input parameters and the return value, have the same exact lifetime. Which means to tell to compiler, Rust compiler, that if A, S1, S2, and the return value needs to be kept alive together. So it can't free, for example, the S1 that is being passed before the usage of the return value is finished, or the same with S2. So in here, in the example, we pass the two values, and then we get the reference back. And this time, the usage is exactly the same as we had in the previous example. It's just that we now told Rust what are the association of the different lifetimes involved. And the lifetimes are more declarative than instructive. So it's like you give hint to the compiler, and it's up to the compiler how it handles those. You just tell it like these resources are related. Don't free them before you free others. Yeah, actually, that's all I had. So I think I have a lot of time for questions, and that's a good thing. OK. Can I ask you a question to the previous slide? Do you want me to move to the previous slide? Yes. OK. You say that you control the lifetime of that to give it pass when the function is already over. In this example, if S1 is returned, S1 has to be kept alive, but S2 doesn't have to be. Can you express that? I'm not used to that. And I was told to repeat the question. The question is that there is a case where S1 is to be returned and not S2. And in that case, we don't care about S2 anymore. And it's the S1 that can't be freed while the return value is still around. That's true. But it's very hard for compiler to find these out. One thing I did not mention is that in Rust, you have something called lifetime elision. Like, one of the code that you saw earlier where I was using references. But you notice that there was no lifetimes involved there. And the reason was that lifetimes are always there, except that in many cases, at least the simple cases, Rust is smart enough to detect, OK, the compiler. And it declares them for you in the code. So you don't have to care for them. But it's like a bit complicated examples like these. Then you will start to, when you have multiple references being passed and then you have a return as a reference as well, then Rust will not know how to handle that. Because it can handle it on its own, but not through the lifetime syntax. Because it's more dynamic. Like, we don't know which string that will be passed is going to be longest. So it's more like a dynamic decision. And Rust does all things statically. So it needs to do it this way. But if you can't express that one of them has to say that if you can't express it, people have every parameter always. No, you can have multiple lifetimes. That's true. But in this case, you need both of them because you don't know before you know which parameters will be passed. Because it's more dynamic thing. Like, you don't know it. When you're compiling just that function, let's say in a library, and it will be used by people you don't know. And you don't know what will be passed. So you will only know dynamically which case will it be. So that's why it's not possible. No other questions? How do you specify different lifetimes to use a different method? You put like in there, in this example, you have like lifetime A. You just put a comma, lifetime B. So you can declare multiple and then you can use them like the same as the, I'm using A lifetime. Sorry, the question was if I can have multiple lifetimes in there, and that's what I answered. Sorry. The question is if the lifetime is part of the reference type. Not exactly, but it's always there like when you pass things by reference. There's a lifetime involved. It just, the difference is whether you declare, you have to declare it or not. Yeah, actually it's implemented through something called generics in Rust. If you look into generics, you will see that the syntax of declaring them exactly the same and that's how it's implemented. If you might want to read up on that, it's pretty interesting stuff, how lifetimes are implemented. Very good code. Is it possible to have functions that override each other's lifetimes? So we have an object that, well not override, but they have some kind of a lesion. So one function accessing the same resource. So the question is if I understood correctly is that in multi-threaded application, is it possible for one thread to override the other resource of others? Is there a risk of one function as soon as a lifetime should be? No, you can't really mess up with lifetime, no. Are you sure? Yeah, I'm sorry. No worries. Thank you very much. No worries. Thank you.