← All transcripts

Beyond Memory Safety: How Rust Guarantees Code Correctness Transcript, AI Summary & Key Points

InfoQ · 29 days ago · Science & Technology · 46:56 · EN

AI Summary

Rust provides benefits beyond memory safety by making software more correct and reducing developer mistakes through compile-time guarantees. Its enums, ownership, borrowing, lifetimes, resource cleanup, protocol-oriented types, mutex guards, typestate patterns, and generic builders allow invalid states and invalid sequences of operations to be rejected by the compiler. These techniques can improve robustness without runtime overhead, although Rust's complex checks can increase compile times.

Key Points

  • Rust's value extends beyond memory safety because its type system makes many developer mistakes and invalid operations difficult or impossible to express.
  • Rust's core model centers on data types and functions, while omitting concepts such as garbage collection, classes, inheritance, traditional object-oriented programming, function overloading, and type coercion.
  • Rust enums can store different data in different variants, and exhaustive matching forces every variant to be handled.
  • Optional data can be modeled with an enum containing a no-data variant and a variant that stores a generic value.
  • Application state can be modeled with enums so that each state carries only the data applicable to it, and constructing a state requires providing its associated data.
  • Every Rust value has exactly one owner at a time, and ownership is checked statically at compile time.
  • Moving ownership prevents a value from being accessed through its previous owner, which can prevent double use of uniquely executable resources such as jobs.
  • When an owner goes out of scope, its value is dropped, allowing memory and other resources to be released according to a known lifecycle.

🔒 13 more in the full analysis

Links mentioned

🔒 Full analysis locked

Unlock more videos and the full analysis

Buy credits to process more videos. Each run includes the full analysis, not just the summary — and you get access to the locked analysis across the library.

Inquire for details →

From this video

1 product

InfoQ

Transcript

Searchable transcript of Beyond Memory Safety: How Rust Guarantees Code Correctness — InfoQ (46:56). Search for a phrase, then click its timestamp to jump straight to that moment in the video.

Captions sourced from the original video on YouTube, published by InfoQ. The video, its captions and all related intellectual property remain the property of their respective owners; AINotes claims no ownership. Provided for research, accessibility and search — see the Transcript Notice and Copyright Policy.

00:05 All right, everyone. Thank you for coming. Thank you for deciding for this slot. I see it's it's a good turnout. I'm happy that you're here. I was told there's no formal introduction. So, just a super quick introduction. My name is Andy and the last couple of years I've been working in the fields of autonomous systems. That's also where I will be bringing in a couple of examples throughout this talk.

00:30 But I think that's that's all about introduction about myself. I think we're all way more interested in the topic itself. So let's get started. And yes, this is a talk about grust. There must be a talk about crust at every conference of course. So this is your obligatory grust talk. Welcome to it. And I want to start with some personal experience. So, I've talked to a lot of people about Rust and I noticed that either they don't like it at all or they like it very much.

01:01 And the people who don't like it usually didn't work with it a lot. They maybe tried it out once then put it away because they didn't get it immediately. But everybody who stuck with it, who tried it out for a longer time, maybe even for a real project, they ended up loving it. Like I still remember two and a half years ago we started a big new project.

01:24 People from different languages being moved into the team and the project was August and one of my colleagues he spent the first two weeks coming from C++ complaining every single day to me but then after about 3 to four weeks it started turning around he started really liking it and now says he never wants to go back. So there must be something to it, right?

01:44 To all those stack overflow surveys where people say it's their most the most loved language. So what is it about it? Is it the memory safety? Well, memory safety is great and I highly benefit from it in the projects I'm doing, but I think there's more to it. What if I tell you that Rust makes it so much easier to write software that's more correct in the first place where it's more difficult to make easy mistakes to make developer mistakes software which is overall more failp proof.

02:19 So this is what it's about today and where I want to go in a bit more detail with you on what is there beyond memory safety in Rust. Rust itself I believe is in its core a rather simple language because you you know most people that haven't worked with Rust that just have heard about it they know two things about it that it's memory safe and that it's supposed to be super hard with such a steep learning curve.

02:50 There's some truth to it. I have to admit it's not the easiest to get into. But if you look at the core of it, it's really all about data and functions. So data in the sense of you have some types which have some data and functions which do something with your types with the values with the data. And a lot of concepts from other languages that make it more complicated are just missing like no garbage collection, classes, inheritance, traditional object-oriented programming, no pointers, function overloading, type

03:23 coercion, all those are missing. So in its core, I've heard some people comparing it to being a bit more seal like actually or nowadays also zig. But that's up to you to decide how it feels like for you. But before we dive dive into those gr specific concepts, I want to start with enums in gust because enums in gust are more than just integers. In gust enums, variants can store data.

03:56 So this means that every variant of your enum can hold some specific data. So it can have no data at all or it can have some data which is different from the other variants. This is a bit comparable maybe to some people know it from like TypeScript with tech units but in Rust this is also a first first class um concept there. And in order to access this web data, you have to match the enum.

04:23 And matching means that it's similar to a switch statement, you know, from other languages where you can have multiple switch arms or like cases where you check against what value does my enum actually have. And when you're in this switch case or in a match arm, then you can grunt some logic. But in addition to just being able to then just run some logic, you actually get access to this web data.

04:51 So only if you're in the correct match arm, you can the compiler actually lets you access this data. And matches must be exhaustive, which means that you're forced to handle every single variant. This doesn't mean explicitly every single variant. It can also be through catch alls, but everything needs to be handled, which prevents you from forgetting something.

05:13 Of course, there's also some additional syntax available which makes everything a bit more easy and ergonomic in special cases. So let's look at what we can actually do with it. And there's this concept of optional data which is present everywhere. Like often it's modeled as null pointers, null value, nil value or even specific standard library types which provide some utility around this concept.

05:36 In Gust, we just model it as an option. That's how it's done in the standard library. There's no compiler built in for it or something like this. It's just an option. You could write it yourself in less than a minute. So an an option in Rust is defined as an enum with two variants. The nonvariant which has no data associated with it. And the sum variant which can hold some data T.

06:00 So T is just a standin. It's a generic. We will talk about it later. But T just means you can replace it at compile time with any type you want to. And due to those rules about accessing the data, the optional data is protected by accidental access. So you have to match it. For example, in the field I'm working in, we have robots. And a robot robot can have an active job.

06:26 So it can either do something or currently not doing anything. And we can just model this by an enum by an option. So in order to do something with the active job, I have to match on it. And you see when there's none for example in this case I just return I do nothing but if there is an active job I now get access to the job here and I can do something with it like publish an update to some external system but let's go further state is present everywhere and one intuitive way of model state in Rust is also with enums

07:02 because in a sense all you want is some kind of marker which tells you what state am I in? And you want to have some specific data that's only applicable to those specific states. And in GS we just model this as one big enum with those different variants where each variant is now a single state and can rep some data for example again an example from robotics.

07:29 I have this robot state and it can be un uninitialized. So I just know that it's there. It can be initialized at which point I actually received a position from it and I know where in space is it and it can also be executing a job. So we just model it as just another variant which now also has a job inside of it. So the data now is not only protected by accidentally accessing it while being in the wrong state but also the other way around.

07:58 If I want to transition to a new state I have to provide the data otherwise I cannot construct this variant. So I'm also ensured that that I only construct valid states in the first place. Everything else is prevented by the compiler. That's it about enams. Now we go to the first big concept. Maybe you heard about it, but ownership is a core concept in Rust which you do not really find anywhere else.

08:26 But ownership is actually quite simple. All it means is that each value in Rust has has an owner. So it needs to have an owner. It cannot have no owner at all. And it is only allowed to have one owner at a time. And this rule is enforced by the compiler. So it's statically shaped checked at compile time. But what this also allows us this concept of ownership is that since we know who's the owner of our value and since we then can easily track when is the owner created and when does the owner go out of scope think of a

09:04 variable. It's not so hard to figure out when does a variable go out of scope. You can now actually track the life cycle of your value because when the owner goes out of scope your value would be out of an owner. though it is just dropped and dropping just means it's destroyed in some way like memories freed or other resources are released but ownership can also be moved and this is where things start getting a bit more interesting like in this example you see here whenever I do an assignment this automatically means a

09:41 transfer of ownership so the data is not copied it's actually the ownership is moved. So you can also say the data is moved but not in the sense that the data itself is being moved to some other memory location. This could happen in function calls for example but it usually just means conceptually that you move the ownership from one owner to another owner.

10:05 Meaning the old owner gives up its ownership and the value can no longer be accessed through the old owner. And we can do already some interesting things with it. Something you can prevent with it are what I call double uses. And double use for me is this concept of you have some kind of entity which is only allowed to exist once. Not in the sense of a singleton but rather a unique instantiation of some some value like again a job in a in a system where robots are executing jobs.

10:39 You can have multiple jobs, but each job can only be executed once because after it was executed, it's finished. It should no longer be able to work on it. And only a single robot is only ever allowed to work on a single job. And the single job is only ever to be allowed to be executed by a single robot at a time. So how can we model this in Quest? Let's first look at the function signature.

11:07 And that's what we'll do a lot in this talk. Look at function signatures. We will look at the function signatures of the assigned function on a robot. So ignore the first part about the self. This just refers to the robot you're calling it on. But now look at the job. So we define a parameter job and it takes a value of type job and there's no reference symbol in front of it which means that we take this job by value.

11:32 And if you remember from the ownership rules, this now means that this parameter uh this parameter job takes ownership over the value job. So if I pass it in from the outside, the ownership is moved into the function or even more detailed, it's moved into the parameter job. What does this mean in practice? When I now want to assign a job, and let's assume we have some kind of queue where those jobs are stored.

11:56 I fetch myself a new job by popping it from the front of the queue. And here we already see ownership in action because the Q used to be the owner of the job value, but now I'm pop it from the front of it and assign it to the new job variable. At this point, the queue gave up ownership over it. Of course, this also means practically in the inside that it got moved out of its internal buffer.

12:21 So some pointers are being rearranged, maybe some memory allocation change. But ownership wise, it means that the job Q no longer owns the job. And now we can assign it to a robot with the function we just discussed. But now if I try to assign the new the same job again to another robot, I get a compile time error because we try to move use move value in the assignment to the first robot.

12:50 We actually moved the ownership of the job out of the new job variable now into this robot one function. And that's how we can effectively prevent what I call double users in Rust. I also talked about the life cycle of values and the life cycle can be more than just managing the memory because we know the life cycle of each value. we know when it is created and when it is being dropped.

13:21 This also means we can associate resource managers management um with this life cycle. Rust allows us to hook into the drop of a value. So dropping again means this event when the value goes out of scope. So when the owner goes out of scope and the value would no longer have an owner, the value is being dropped and we can hook into this through the drop function.

13:48 So we can define some special behavior for it. In this robotics field, you often have in 2D space some regions, for example, some tight space around a corner where you say there's only one robot allowed in there at a time. So you want to manage a real world resource like in this case a 2D physical space and want to make sure that this guarantee is actually upheld in your code.

14:16 How we can model this in Rust is we have some kind of GRE tree which manage manages those accesses and we request access by providing a zone ID and what we get back is a zone access. The zone access is a custom type I implement and it acts kind of like a token. When I have this token, I am allowed to drive into the zone. And since since the zone access cannot be copied or cloned, it can only be moved around.

14:44 This means that we can only ever have one zone access alive at a time. And I can, for example, hand it around between robots, and that's all fine. But it's not able to I'm not able to accidentally give two robots the same zone access. And now it goes even further. There can be many cases where I have to free the zone because for example the robot disconnects and I have to handle this case then would have to handle this case explicitly in code by saying okay if it's being disconnected I have to free this.

15:19 If the robot changes state I have to free it. If this then free it but instead of having to manage all those individual paths and code I can just hook into the life cycle of the zone access type and say when it is dropped then we just free the resource. So if you look at this code in the but on the bottom we say in this drop function that you have some reference to the zone registry and if you're being dropped just call free and the zone registry will handle for example communicating to other systems or marking it as

15:50 free in its internal state and this means that since our robot when it takes ownership over the zone access is being dropped so the robot has ownership of the zone access and if it's being dropped itself maybe [clears throat] because it disconnected and we just deleted from our uh application state that since the robot was the owner of the zone access and the owner now itself goes out of scope.

16:19 This also means that the zone access itself goes out of scope and the compiler just takes care of for you of freeing all the resources associated with it. And this makes it so much easier to handle those kind of real world resources which are more than just memory and prevent all those all those um leakage problems of you accidentally didn't consider this one path in code where this could leak because it's handed for you.

16:48 But often times we do not just care about owning data. Sometimes we just want to reference data because if you could just own data the language would be quite limiting and that's where we get to borrowing. Borrowing is just a way of safely referencing data but with some rules and this rule is that there can be either a single mutable reference to some value at a time or any number of immutable references.

17:17 So this is important for memory safety of course because if there's only ever one mutable reference at a time this means that there are effectively no data graces from multiple places in code trying to access the same variable. But we will see that this can be only also used for more than just memory safety. So as the name suggests with a mutable borrow I'm allowed to mutate the underlying data.

17:44 with an immutable borrow. I'm just allowed to look at it. And again, borrowing does not move the ownership. The original owner stays the same. Instead, you're just referencing the value. And this goes hand inhand with lifetimes, the next concept. So, lifetimes tell you, is this thing still safe to use? So, is this burrow still valid and safe to use at this specific point in time?

18:15 What this means is that a borrow's lifetime, so a reference lifetime cannot not outlive the lifetime of its lender. So that's for memory safety of course. So you cannot reference a value after it was being freed. But there's also more to it. We we'll look at it soon. But remember the the lifetime like like the life cycle of your values. A variable's lifetime always begins when it's created.

18:43 and ends when it is destroyed. And this information is then of course used for checking those lifetimes. But also most of the time we do not have to refer to lifetime explicitly because the compiler takes care of it for us. Sometimes though we have to and that's this tick notation of a tick and then some letter or a word just to remember for the next slides.

19:09 Now using ownership, borrowing and lifetimes together we can do something in my opinion very interesting. We can start embedding protocols so runtime protocols at compile time into our types. Here take um a fascinating example or like super interesting example from Rust's primary serial serialization library. Specifically, we will look at a serializer.

19:39 So, a type which facilitates serializing a single value. I simplified it a bit. So, if you look into the actual library library which is called serti, it's a bit more complex, but this captures this core behavior. So, we start with a serializer. So, whenever I want to serialize something, something else passes me a serializer. And the serializer implements multiple methods for all kinds of high level or like general kind of kinds of values like for an integer 32 for float 64 um for an enum but also for strct and let's

20:18 look how the serialization serialization of a strct would work. We have the serializ function and it takes self by value. So instead of just referencing the serializer instance that we are calling it on, we're actually consuming the serializer instance itself, which means that I call this function and after this I won't be able to access the same serializer again.

20:43 And look at its return type. It returns a serialized strct. So you can kind of see it as a transformation from it transforms this original more generic serializer into a more specialized one which is specialized for serializing a strct and this strct serializer now implements two meth methods itself. One is called serialize field which is for serializing a single field.

21:08 I simplified the signature a bit um but the core of it is that you see how it takes immutable reference to self. So you can call it over and over again because it does not consume the inset it's being called on. You can keep call it over and over again because it just references it. And this is expected because you can have multiple fields you want to serialize one after another.

21:34 But then look at the end the end function. The end function marks ending or like finalizing this serial serialization step. And this can actually have a lot of implications for a real serial for a real protocol you want to serialize. It might mean that you now finally replace the closing brace in in JSON. It can mean that you compute some kind of file header that's being written which has the total size of your strct in it in bytes.

22:02 So it holds some gravity calling this n function. And often times in serial ser serialization libraries you then have for example a dock string which tells you do not call any other functions which serialize data after calling end. And you might even get a crash out of it or if it's better built this library you give a get a runtime error. But in Rust this is not necessary because we can just say the end function consumes self.

22:33 So it consumes the serial strct instance in terms of ownership. This means that the strct serializer or the value that holds the struct serializer gives up ownershizer and moves it into the end function. And in the end function we then basically just do our finishing and then the value goes out of scope and is being dropped and it cannot be used again.

22:54 And now look at this in practice how this would actually look like. So I have a strct serializer I get it from somewhere and now sorry I I have a normal serializer and get it from somewhere and construct a ser a strct serializer by calling serial strct. Next step I do number of serial serializations of fields. Now I say I'm done and I want to finish it.

23:23 So I call end. This remember now moves the ownership from the strct serializer variable into this end end method but it's not returned anywhere. So the lifetime now of the strct serializer ends and it's being dropped. And if I would try to call it again we actually get a compiler compile time error which tells us borrow of move value strruct serializer.

23:50 And this I think is super interesting because you eliminate a full class of of developer bugs completely at compile time. You save a lot of error handling you would have to do otherwise and all this by embedding this kind of runtime protocol into your type system and the gr type system makes this possible. Another example which is related to this is the one of modeling um the is of modeling this kind of access to some shared data through mutex.

24:26 Oftent times this is modeled as two separate things. one the data you want to protect and then a mutex where you have this contract with the developer that in order to access this value you have to lock the mutex first and all you if you locked it you are allowed to now for example dreference a pointer in rust this works a little bit more different and a bit more robust so first of all let's look at the new method which creates us a new mutex you see again that there's no reference in the signature which means that the

25:00 data we want to protect is moved into the mutex. So we give up ownership. We can no longer refer to the like reference the data or use it from outside of the mutx because we give up ownership over it and move it into the mutex itself. Now if we lock a mutx and the lock is locking is successful we get back a mutx guard. And here we for the first time see an explicit gra an explicit lifetime.

25:26 this tick A and this tick A means that the lifetime of the mutx guard is bound to the lifetime of the mutex itself. So to break this down a bit, this means that the mutx card is not allowed to outlive the mutx itself. So I'm not allowed to hold the mutx card for any longer than the mutex itself is actually alive. But it gets even more interesting. I can access the data now through the mutx guard by just dreferencing the guard.

26:04 But also I get just a reference to just a reference to my data. And by lifetime rules this reference of my underlying data it's also bound to the lifetime now of the mutx guard. But of course since the reference of the mutx guards and the lifetime of the mutx guard is bounded by the lifetime of the mutx itself. This also means that this lifetime of our borrow here of of the reference we have can also not outlive the mutex itself.

26:33 So this is memory safety. But it gets more even more interesting I promise because the mutx guard unlocks the mutex when it goes out of scope when it is being dropped. And now let's put all of this this together. We know that our reference to our underlying data which can which we can only get through a mutx card cannot live longer than the mutx card itself.

26:57 So accessing the reference to our underlying data after the mutx card was dropped would give us a compile time error. But since the mutx guard itself when it goes out of scope unlocks the mutex, this means that it's impossible for us to obtain or use a reference to our protected data when we do not own a lock. And this is super interesting because I've worked on multi heavily multi-threaded code and so many cases where people accidentally held a reference passed the ground and forgot about it and then unlock the mutex

27:32 and you suddenly have a concurrency problem. Cool. The final concept for today I promise and this is the concept of generics. So generics we we've seen it for example in the option type generics are standins in grust. So there are definitions that can be reused with different concrete types. For example in the option it can be replaced or another example is a vector.

28:10 So it's often used in collections because of course you usually just want to implement the type once and then we use it with with all sorts of different types that it's that it's storing. Generics uh always replace at compile time with concrete types through a process called monommorphization. This means that for every specialization some specific code is being generated.

28:37 So eliminating any compile uh any runtime overhead, it's just as fast as working with concrete concrete types in the first place. And we could also do more with generics. We can bound them by traits and lifetimes like bounding something by lifetime. We've seen this before with a mutx card, but also by traits and traits are just a concept of saying what kind of behavior must a type support.

29:00 But that's not so interesting for us today. Let's revisit state machines. So I told you that state can be easily be modeled in Rust with enums and this is true and super practical in a lot of cases but in some situations a different pattern can be more helpful and this is using the type state pattern. So type step just means this kind of concept we've already visited of encoding state information at compile time into a type system.

29:38 Now let's look at it into more detail. I define three new types. An uninit type in a type and an executing job type. Those are the same same kind of states we had in our original state machine in the enum just encoded as variants. Now the unended type we remember we said we don't have any data associated associated with it. So here it's just an empty strruct.

30:04 So it's a zero size type. It doesn't take up any space. You can kind of just view it as a marker kind of a marker that's used in the compiler as hey that's some in some specific type. Now we have the inner type which could have data like uh the position we talked about and another type called the executing job type which then has in addition to the position also our job that we're currently executing.

30:31 And now in we could use them as they are and just define state transitions between just those types and this would be completely fine. But often we also have some additional data that's associated with this thing we're trying to model the state of that we also want to store somewhere. Like a robot can have a name and I want to refer to this name and use it somewhere.

30:54 So instead of just using those types by themselves, I embed them into now like a higher level type. In this case, let's say that's just a robot. And a robot is generic over some state s. Those are those angled brackets and those S. And now I can use those S as a type standin in for example my fields and say okay this robot has a state and the state is of type S.

31:18 But at this time I do not know the state yet. I just know that it will be some kind of state some concrete state that will be specified out at compile time. And I can implement behavior for this robot in this implementation block. And notice how I do not specify out the S. This means that this functionality works for every kind of robot. So for every robot in any state, it makes sense because we said that we always have a name available of our robot.

31:47 So it shouldn't depend on what state we're in. And that's what this is modeling. But now let's assume we in the uninit state. So I create another implementation block. And here I specify out the generic to be of type uninit. So we're no longer talking about a collection like a set of types. I'm talking about a specific type a robot uninit. That's how what I will just call it now.

32:16 A robot uninit type. And for this I implement an init function which takes ownership of self and returns a new robot in an init type and also takes a position. So I'm kind of like just constructing a new robot type. Of course, often times you only have then some additional functionality or behavior that runs under the hood, but for simplicity, it's kind of just like a constructor now.

32:45 But we could also now look at the robot in in an init state. And there we say, okay, we do the same again. state transition becomes now a method implementation on this concrete type an a robot in it and there we again take ownership of self. We take the data we either then need or maybe this data is computed in the function itself from somewhere else and then we return a new type a robot in executing job state but also again here we say we have a position available so let's make this available to people from the

33:16 outside by implementing a public method just called position and now we return the position so instead of putting this position method on the generic robot where we would have to go return it as optional data and you as the caller would always have to check is this there or is this not there or even um then do all this error handling when you know it must be there but I do not get the correct type so I have to like force it into the correct type this makes it so much easier because you know in this init state you must

33:49 have a position let's look at this in practice we get a robot unin it I just pop it from some some theoretical queue that we have and it's an in the uninit state. So I explicitly actually marked it as in unin it to give you a hint of what type this is and I can call the name method on it just fine. But calling the position method on it now gives me a compile time error saying no method name position font for the strate in it in the current scope.

34:25 And in this error message there will be even more. There's even then s suggestions telling you oh yeah but you know this method is available on a robot in in it or robot in executing job state. So the compiler actually helps you out there a lot. Last example I promise and this is now even a bit more advanced and this is leveraging this exact exactly same kind of pattern for builders.

34:53 So for you who don't know what the builder pattern is, it's just a pattern where you say you have some rather complex type or some type which has a complex creation flow. For example, an HTTP request where you say that I want to set headers, I want to set a body and then I want to finish it in some way and I have to do some validation on there if this is correct or not.

35:14 So you do it in two steps usually. You create a builder type and this builder type facilitates this building process and then you finish up this builder by calling some kind of finish method which then returns you the final type. Let's look at how we can model this in Rust. I will use the example of building a robot simulation. So a simulation of a single crot and let's say we need to provide it two kinds of information.

35:42 its initial position and the kind of map it should be using. So the kind of map it's driving on. We create a robot simulation builder type. And here we use generics again, a P generic, so a standard for position and an M generic for the map. And let's go one step back again. So I defined those four strcts and all those are zero sites. So they don't hold any data.

36:10 They are just like markers for the compiler. no position, position set, no map on map set. And we always start out in this specific state of no position and no map. So this robot simulation builder with those two specified out generics which are replaced by no position and no map is just is now a concrete type. And on this type I can say what I can do is I can set a position or I can set a map.

36:42 And let's look at the function signature again. And that's something that's probably for you know recurring but we consume itself to invalidate the previous value we had. And we transform it now into a new type and in the set position case for example into robot simulation builder where we change the no position type in the generic specialization to the position set type but we keep no map the same.

37:12 And let's look what we implement now for this concrete type of robot simulation builder position set but no map. We only implement a set map method because we already set the position. We do not need need it again. So we just say set map. And this can be very interesting for cases where resetting the same value is for example expensive because you do some expensive computation in the background or even some external requests or something or where it's just from the contract itself that you're trying to build invalid of

37:45 providing the same value multiple times. So we can prevent this already at compile time during this way. So now only the set map method is available on this specific type specialization and it returns us a robot simulation builder with position set and map set. So we're done now because all we have to do here is just call build and build will now do the final building step.

38:10 We know that we provided everything we need. So there's no reason for having any kind of errors that could be returned. we just straight up return the robot simulation. So what I wanted to tell you today is that robustness does not need to be hard. It can be hard and achieving extremely high level of confidence in a software will still stay a very challenging problem which requires strict processes and expensive tooling.

38:38 But for practical purposes, most of us are not building safety critical applications. We are just building applications that need to be reliable because people depend on them. And Rust helps us a lot in achieving a level of robustness through le leveraging the type system that's hard to achieve with other languages I know of. And that's the end of the talk.

39:01 I hope you learned something. We still have a couple of minutes left for questions. So I'm very happy if or like thankful for any questions you have. Also, if you don't want to ask those questions now, you can ask them later. If something is unclear to you, if you say, "Oh, no. This doesn't make sense what you said," just approach me. I'm happy to talk about it on end.

39:21 All right. Thanks a lot, everybody. [applause] Um, could you go back like one slide to your last example there or two slides? >> Of course. >> Yeah, that one. So this works well if you have two uh two generic elements. Is it I assume it is I hope it is possible that you have like one P and one nom map for example. So you don't have to implement the set map in every iterate like every permutation of your implementation.

39:55 >> Yeah I know where getting it. Of course if you have 10 different kinds of of of things you want to set it gets much more it becomes impractical to write out all per permutations by hand. Rust has an extremely strong macro system. So macros in Rust are not just some kind of text templating, but you can in Rust actually write code that that that then generates new code at compile time.

40:18 So you can actually implement um a macro that does all this method generation for you. >> Okay. So but you couldn't write like robot simulation builder P and no map. That would not work like having one of the parts being generic. >> Yeah. No, this Yeah, this doesn't work. Okay. You have to specify out. >> Okay. Thanks. >> Yeah. You could of course write if if something you could of course write P and then just the generic M not specialized to a type, but it I don't know where this would be useful.

41:03 You mentioned the ownership and borrowing model. I assume this is completely done at compile time and no extra code is added. >> Yep. And also then of course it's not possible to like during runtime reflect like who is the owner of something or >> no there there's no there's no runtime reflection of who's an owner because that's really a concept at compile time that check complete at compile time and at runtime you you do not have a way of looking up what kind of other variable owns my current owns this value that's

41:36 that's nothing that's possible. Yeah. >> Thank you. You mentioned monorization to transform generics into monorphic types. >> How complex or hard is it on the compiler? Does it make it slow? And then follow-up question, >> uh all of these wonderful checks which are done at compile time, how heavy are those on the compile on the compiler? >> Very very good question.

42:06 you're getting to one of the like the core improvements the the the Rust Rust compiler team is still working on which is about compile times. So compile times of course with such a complex compiler take longer than in other languages that's just just a fact. There are a lot of checks that are happening super complex algorithms about figuring out ownership.

42:33 Grass invested a lot into the development speed. So with incremental compilation that you do not have to compile everything from scratch again. So most of the time you're working on a project even it's if it's massive your compilation times are usually in the seconds of course than creating a production build. This can take some time but often times you throw this off to your um continuous integration worker anyway and this takes care of it.

42:58 But I can also tell you that I've seen Typescript projects of web applications where the build takes longer than our big Rust application. >> Thank you. >> Yeah, of course. Okay, any other questions? Oh, we have two more. Um, in an earlier slide you were talking about enums carrying sort of many they could have different states, could have different data, >> those kind of things.

43:42 Coming from a C++ background. Um, are those do I need to worry about like memory locality or things like do I do I have a kind of a collection of data that could exist in many different places in memory? Does Rust you [snorts] know what I'm getting at? like um if I have this enome is collecting data and then I change it into another enum and that has additional data for the state >> now am I am I actually pulling that from memory from many different parts of the application.

44:10 Oh, so you're asking one of the data locality and other are about reallocation of data. >> Yes. >> Now, enums enums are enums are allocated once. So either on the stack or on the heap depending on where you allocate them and the size of the enum is the size of the largest variant. So it's comparable to unions for example in C where the compiler figures out how much like what are the requirements for alignment and for size and everything and then allocates it appropriately.

44:36 So if you then actually modify the enum into a different variant, you usually don't, not just usually, you don't actually reallocate or pull data from different places into there. There are even optimizations to make them as small as possible. For example, in Gust since there's no null value, Gust has this concept of actually then marking types as they those types cannot just be null.

45:03 And this actually is then used in enums in cases where you know that the value that it holds that there's nothing which can be null that you can even then mark some variance as that you can even then have a variant for example where just everything in memory is replaced by a null value and that's how you mark this variant to store save even more data.

45:27 >> Cool. Any other questions over there? I understood that uh Rust has a nightly support for specialization of partial specialization of genetics. Right. >> What do you mean by partial specialization? Uh the first question uh having an implementation that specialize only some of the generic templates. >> Okay. Um honestly I would have to look this upwards currently on on nightly or what not.

46:10 But if you say so then um of course yeah then it's probably a nightly feature currently. there only things like um variatic generics which have been in the pipeline for a long time of being able to just specify generics and say you expect a various number of them which you do not know yet. Um so there's still a lot of work on the generic system of course as well. Cool. If that's it. Okay, thanks a lot. [applause]