 Welcome everyone, this is the first time for me giving such a workshop in front of an online audience, although I give an online conference audience, so thank you all for being here. And I apologize if some of the management of my setup will take a second for me to get used to. First of all, I would like to share some introductory slides with you and get started with that. I'm going to share this screen here and I'm going to get started. So thank you all for being here and listening to this workshop. I'm actually quite happy to give this workshop again because I have started with such a format to start training Rust. So an introduction in two hours and before I'm even getting started introducing myself, if you don't have Rust installed, I would highly recommend you to install Rust through Rust up. This means you can either go to rustlang.org slash learn slash get started. And the reason why I put this slide up front is if you are using, oh, for some people audience seems to be muted. Okay. So the reason why I'm putting this slide up front is if you are on Windows, this requires Visual Studio to be installed, which is a sizable download. If you can't get Rust installed, you can always use the playground on the play.rustlang.org to follow along and at least get things to compile. And I'll explain what Rust up is in detail in a bit, but just for the sake of making sure that you can get your download started and your installation started, I wanted to put this as a first slide. So who am I? I'm from Gaysha. I'm one of the founders of First Systems and Critical Section. I am part of the Rust project since around 2015 as a member of the community and the core team. I'm also currently sitting at the board of the Rust Foundation and have been a part of the foundation setup group. And personally I've been a Rustation since 2013, roughly. And I've created a number of projects in the meantime, such as the Berlin User Group and also, for example, the European Conference Rust Test. And I've been training Rust since 2015 now and recently had a look back on how many people I train and that's like over 600 conservative number people. All our teaching material is open source, though I am not going to use most of it in the part of this course. I don't think that copyright on teaching material is a good forcing function. And the other thing that we do as First Systems, which I would like to highlight is we try to work as much as possible in open source, also using new funding models. So for example, we propose tools to the ecosystem that we think is useful, that we think are useful and then also the idea behind that is that making sure that whenever building a new tool that sustainability as a checkbox that you fix, that you check off first is incredibly important because who wants to adopt a tool that goes out of maintenance in two years. So this is actually, I believe this should be one of the first checkboxes to fix. You see this logo up there called Ferrocin. You may have seen the talk by Sabri earlier. We're also currently engaged in bringing Rust to particularly safety critical sectors. That is the job of our subsidiary called Critical Section. And that is a project that we want to succeed in in the next few years, 2022, 2023, is the date when we want to see Rust on course. About this workshop and how it's going to work, because I only have two hours, the way I'm going to do this is in a code along style. If you want to, you can just watch me doing things and talking about it, but also I highly encourage you to have a text buffer open and just code along. Also like if you've got a minute and some bandwidth, you can try to take chances and start developing in a certain direction or not and just try things out and please ask questions in chat doing that. I've made sure that the example is small enough that I have ample time to actually go and talk about these kinds of things. I'll also be relying on a little of the experience of the previous speakers that they have shared, particularly Nels talk about borrowing and working with the borrower checker, which I will show practical applications of. And the example is betrayingly simple. What we want to build in the next two hours is just a small server that accepts TCP connections, not even HTTP connections. Then read one line from the incoming stream, either until the first new line or until the input stream closes. For the sake of the exercise, it's okay to just trim trailing spaces of the incoming messages. And then if the input is an empty string, we're going to store the string internally somewhere so that we have some amount of state. And if it is an empty string, we'll interpret it as give me one of those messages that someone previously said. And then we want to start improving on that by adding, for example, threading or experiencing Rust concurrency safety, we'll see how far we get. And that is, the reason why I use this example is because it shows every small issue that we're going to deal with at precisely one location. It is simple, but it allows us to always come back and you can keep that and rebuild it and rebuild it and rebuild it, trying out new things that you learn because once you got used to the example, it's easy to rebuild. And yeah, that's about it. And along the way, we will actually learn how Rust's ownership model does not only manage memory, but also, for example, other resources like network connections. We're going to learn mutable and immutable references and how they work and how I can, for example, work with them in a dynamic setting where we need to assume multiple connections to be currently being handled. And also some basic concurrency safety through mutixes and smart pointers. So, thank you. Before I start, I want to give the chance for a first round of quick questions, just quick fire questions, not long ones. Someone feels need clarity. Thank you, I'm taking your slides. I am going to try to remove myself the stage if that's possible. Doesn't seem like. Then it'll have to do here because I would actually like to work with my full screen here. I hope I'm going to increase for that reason the font size here a little, even if that ends up being painfully large and give myself a little bit more space to the right. Let's do it that way. I'll work full screen with the text editor and then I need to take care with the rest of my window management. The wide window that you're seeing here is one that I'm going to use for explanatory drawings so that we can visualize a little bit of around what's happening. And first of all, I'm going to close this part here. There's one question. I can turn off the camera says three, but this keeps my window on, at least at my side. Okay. There's a question, is there a rainbow for this to follow along? Yes, you can. There might be some slight divergences, but you can go for here. This is actually, I'm using this example since a couple of years and there you can see it even like collected in steps. I will also share code afterwards. This one question here, about what's the best text editor for Rust? Them or I personally use VS code for a number of reasons, but you can basically use any that supports a plugin from Rust Analyzer, share a link and chat. Sorry for me clicking. And it has set up instructions for your favorite text editor. And if you feel, and if you are already using IntelliJ or IntelliJ-C line, especially I can recommend you sticking with that and installing the Rust plugin. So some questions in the QA section. As a beginner, you will be able to follow along this workshop. Yeah, definitely. If we use the standard, will we look at minifying the result? No, but I'm happy to take that later. Feel free to send me a message deck and we'll have a look at that as well. Okay, if you have Rust installed, particularly Rust up, I promise you to talk a tiny bit about Rust up and why it is there. I won't go too much into details, but Rust up is Rust tool chain manager. And what it does is it installs Rust compiler versions for you, allows you to switch between them similar to something like RVM or the node version manager, but it also does more. And I think that's the important part around this and where it becomes the tool chain manager. So I can, first of all, I can go and say, Rust up default and can switch, for example, between the nightly Rust channel, if I want to, or between the stable, which I'm going to use for this course. But another thing that it, for example, allows is that Rust by default, all the tooling is cross compilation aware and the Rust project ships a number of pre-built targets. And for example, Rust up also manages these. So I do quite a bit of embedded development on these kinds of boards, but that's not the point of this course. So I have a number of the targets installed to compile software for this board. And here's my native target. I'm on a Windows PC and I use the Microsoft visual code back end. There's also one for MinGW if you want to use that. So if you want to use a neutral chain on Windows and you prefer that, you see there's other ones, like for example, Android, even like Google's new operating system, Fuchsia, the experimental one, and for example, iOS. So Rust up manages your whole development environment and that's why I recommend using it. There's a, some people don't like Rust up because you need to install it through a shell script. This is only the case if you are operating system or your Linux distribution does not yet ship Rust up in its package manager, which is if it's available in a package manager, the way I recommend it. In difference, in difference to, for example, the compiler that comes with your Linux distribution, this one is your development. This one we consider your development compiler. Your Linux distribution now often has a Rust C version as well. This is in general, the one that is used to compile other packages just for the difference on what Rust up does and while there's still one in package managing. And this one I would recommend to use if you're actually interested in testing your software on a specific Linux distribution. And one thing that that is just to quickly address the question by Olga, what would you recommend as a container base for Windows based build? I usually don't build in containers when I'm working on Windows. I use the native target because I'm very interested in cross-platform development. That's a personal habit though. I'm a little bit out of the cloud space in the recent years. So I can actually omit that. The Rust project itself ships Docker images though with the compiler in it. Yeah. And this is where I would like to get started. So if you have a directory somewhere in which you're going to take this workshop on, I would like you to navigate there and there put in cargo new TCP mailbox. And we get entered there. Cargo will create us a new binary application. So this is something we can run called TCP mailbox. I'm going to navigate down and finally open my editor there. And give you some seconds to follow. So the first thing I would recommend you to do in this directory is put in cargo run and it should build this and print how a world. And if that worked, your tool chain is working and should continue working through the whole workshop. And if anyone has any issues there, please let me know in chat. Maybe someone has also seen this issue and may help there. Also, if you do see issues in this process, we're interested in the bug report because this should work in all cases. And if it doesn't, the Rust project considers this a bug and would like to know what you ran into. Let's have a quick look on what's in this directory before we go into the main RS file and we're not going to leave it for most of this course. See how far we go. So what this creates is a new cargo project. Cargo is Rust's built tool, built management and dependency management tool. And it has a manifest file called cargo tumble. Tom's obvious minimal language, a kind of an any file language. I think most people know tumble nowadays. And what it takes is a top-level key called package, gives the package some metadata, the name, version. This key selects the language profile. The Rust compiler understands two language profiles, the 2018 and the 2015 profile. You can completely ignore the 2015 profile nowadays. No one uses it anymore except in very niche cases where code hasn't been ported over yet. So this is something, what I would say, learn on need. If you're learning Rust, learn Rust 2018 and don't bother unless you probably never run into the case where 2015 becomes relevant for you. And below we have a dependency section. And in this dependency section, we can express dependencies similar to RubyGems, NPM, any other package manager. What we're not doing to express one now, we would like to start coding. And for that reason, we're going to go into the main rs file and we are going to fold the left side here so that it doesn't take up space. And yeah, so this is where I would like to get started. So the first thing that we need to solve is we want to set up a TCP server that starts listening for connections. And for every new connection does something. So that's our first initial goal. First thing we're going to do is write a small accepting loop that just on every new connection prints high new connection. For that, I can highly recommend after this course to actually follow down a little the types that I'm going to use here and have a look at their documentation in the standard lip. I'm not going to do that for the sake of time but our standard lip is completely documented. And again, if you read something in the documentation and it doesn't immediately click or you have a hard time understanding it would also be very interested in a bug report. So the first thing we need to do is in some way get our start listening for network connections. And the way we do this is we use a type called TCP listener, not TCL listener. Can see your standardizer will immediately tell me standard, do you mean standard net TCP listener? That's the one. And if I click that one, it will automatically import it in Rust, you need to declare every type that you use in an unqualified way. So if I just want to use the name TCP listener I have to import use standard net TCP listener. And the function that I need to bind to a socket is what the bind or to an address is the bind function. For the sake, I will use a TCP for v4 address but you can also enter any other kinds understands IPv6, whatever can do so. And we need to bind this result to a variable. Now let's have a look at the result type like what we get back. I have a look here listeners now results TCP listener error. And that's the first time in Rust that we get actually in touch with the result type on the very first line. Rust has a special or this is actually not special is just a type that is provided by the core library says that extract over things that work or may not. So here says this gives back a result of TCP listener and error. The error in this case being the error that for example, we were not able to bind to a socket. In this case, I'm going to use a strategy for result handling is called unwrapping. What unwrapping says is either this worked or immediately crash. For beginners that sometimes feels like this is a way out like I ignore the structured error handling that Rust does. I do not fully subscribe to that. Calling unwrap is basically a statement that if this doesn't work, then the program should immediately quit and shut down in a structured way. So it is perfectly fine to do that. Also for the sake of this session, I will actually handle all result types using unwrap. Why? This is actually for even for experienced Rust developers an easy first pass. I just assume everything works all right. And then I can later go look at all of those unwrap calls and turn them into structured error handling because then I know all the errors that could happen in the program that I have. And that is a good moment to collect them all and put a more structured error handling on top of that. So that is something if we have enough time that I'm actually going to do. So I don't want to omit that part but let's see how far we get. So I get a small warning that I'm not using this variable. Don't let warnings confuse you too much. The Rust compiler takes care that all warnings are relevant once you've done. So currently, hey, you're not using this. I can ignore this warning. I will use that variable in a second. But once your program is done, it is actually a good practice to see that it's warning free because again, all warnings should ultimately be relevant. So just on these two quick questions. Donald is asking, does the ability to have a function called expect? And then you can say give a customer a message. That is working similar to unwrap. That is a choice you can make if you want to. I'm usually, because I am not using this, I want to remove this at some point. I'm just using unwrap right away and then I'm filling that later. On Jack's question, why unwrap was this question mark? This is possible. This is actually something that I will do once we go to more structured error handling. But currently, I want everything to just explode at the place where it happens. So during development, I actually, like if errors stop the program at the place where they happen for initial coding and debugging, I find that more helpful. So they have different behavior. Question mark is a different way of error handling. And the question of Victor, will Rust see through warnings for easier search of unwrap usage? Rust has a code linter called Clippy that actually has a lint that says I want an unwrap. So it can use that. Okay, thanks for the questions. Let's continue. So we said we want to accept all the incoming connections and the way this works is we actually start looping and listener has a method called incoming. And what incoming does, takes the listener and turns it into an iterator. And this iterator can be used to iterate over all incoming connections. So I can use a for loop here. And the reason why I'm saying connection attempt is this only represents the attempt to connect. So later, if we were to write a fully fledged service, this allows us, for example, to react to the condition that the kernel started rejecting connections for whatever reason, I will still see the rejected connections here. And this is the one place where I would actually like to look at what that looks like. If we look here, connection attempt is again, a result of a TCP stream. This is now the connection has worked and an error. And I would like to hear to handle this case so that you see how Rust enable us to handle this case in a structured fashion. And the way you do that is you use a match statement. Rust's match statements allow us to look at these types called, yeah, these results types and also all other types that we're going to introduce of a similar form, I'll introduce one later. And say match connection attempt. And if I have a look here, this one is an error currently, the error being missing match arm. So what it tells me is there's multiple options and there's multiple alternatives that you need to cover which that type could represent. And I can easily get them inserted by my IDE by clicking here left and say, fill match arms. And now I have those two conditions and result is a type that says either everything went okay, then you get the left value or there was an error, then you get the right value. Let's start with the error first. I can find this error to a variable called E and then use E print LN. So similar to the print LN that we've seen just at the beginning with the Hello World can use E print LN to print error connecting and E print LN prints to standard error instead of standard out. And using the interpolation syntax here, I can print E. There's a question in the chat was, does the bang mean E print LN? E print LN and print LN are macros in Rust and macros always need to be called with a bang on them. That's it. So you can get the Rust compiler to actually expand this macro for you and see what it does. In the back, what it does, it gets the standard error handle locks on it, then writes to it and releases the lock for synchronization reasons. You could start writing raw. And yes, you can add custom macros like in C though macros and Rust are not textual replacement. There's a structured macro language behind that. And in the case of okay, now we get our stream. So this is our connecting stream. And this is where I want to exit this function here, the main function and call the function called handle client. Now we have a connected client and pass that stream in and the function is not here, but we can again use our IDE or write it by hand to generate the handle client function here. There we go. Now we have this handle client function and can do whatever we want here. First of all, one thing that I would like to do is we move the standard net here and import the GCP stream directly. So that I don't need to fully qualify it because I'm going to use it again for other functions. Rust has this nice to do macro that allows you to have parts of your code unwritten. And if you hit to do your code will just crash and say there's an open to do here. So it's a piece of convenience here. And there's an important thing here. Rust has famously this ownership system. And if you just pass in a bare type into a function, we say it takes ownership. So this function now takes ownership of the connection. And what we can now do is, example, we can print here client connected and then go and actually start running the program. So let's run it and let's connect. I'm just going to use telnet here. Cool for quit. Say for that. And let's connect to 7878 and it will have a connected client here. Also immediately quit. And the reason for this being is precisely Rust's ownership model. I have not explicitly said goodbye to the client, but this handle client function has taken ownership of this currently active TCP stream for that client. And one thing that Rust does implicitly, but I can also annotate that explicitly is once an owned value falls out of scope, it will deregister its interest in the resource being managed, which in this case is the connection. So the moment the handle client function ends after printing to standard out, it will throw the stream away and in the process of it, closing it. So yeah, that kind also works. Yeah. So that makes sure that we cannot accidentally forget to close this TCP stream. And we'll make sure that we disconnect the client properly the moment we let the stream drop. We'll remove that again. This is just an illustration. So this is our first experience with ownership, direct. We make this connection attempt. We see if it worked. If it worked, we'll take the connected TCP stream out and we call a function called handle client. And at this moment, we pass on the ownership of the stream and say, okay, now you are completely responsible for managing that. And this has an expression in the actual software architecture in that we have our bare network system here on the left, sorry. So a connection handler or a system that handles our network connections and we have a handler for those connections on the other side. And if you pass ownership, that means we express a low coupling between those two components. The net component is now not responsible for handling the stream anymore at all. The handler is now responsible for doing that. And that ownership transfer is complete in the sense of the one component is not allowed to use it anymore. I can actually try that out if I were to try doing something with stream over here. For example, try to debug print it. Just even that, I'm not allowed to. You will see an error that says value borrowed here after move and it will call me out here. You have given up this value. And this is the way Rust structures programs values flowing through the program and components giving up ownership and giving it to another component. I agree, full screen would help here. I did not anticipate, I'm very sorry. Let me increase font size a little more. I hope that makes things better. Okay, so now that we've got this TCP stream, I want to start doing what we actually wanted to do. So first of all, let's read a line from this TCP stream. And I'm going to talk about this in a second. And we're going to generate a read line function. And we're going to hand back from this function is a string. This is the line that we read. And this is a place where I need to get a little bit, like pull a little bit out of my API knowledge. Reading a line from an input stream is not available on raw input streams directly. I actually need to use buffered reading. If you're used to Java or other languages, you may know the feeling, we need to wrap the stream in a reader that allows us to buffer this kind of data. So we want a buffered reader and that is available under the name buff reader. And I can create this again with a new function passing in this stream. Now, what does this ampersand mean? This ampersand means I am borrowing here. I'm just giving a reference of the TCP stream to this read line function. Why do I want to use this? I want to reuse this stream later. I maybe want to write to the client again and do other things with this stream. So I do not want to throw it away. So I do not want to pass on the handling of this resource. So I give read line the ability to use that resource through a reference, but I do not want to give it away. So every time you see a type that has a kind of sigil, ampersand or something like that in Rust, that means this is where the referencing system takes place. There's a question here. Does the stream pass above a reader a reference? I am choosing to pass to Buffered Reader a reference. It would actually take both ways, but that's getting a little bit into the words here. So I am choosing to pass in a reference here. Now Buffered Reader now has a function that is called read line that correctly reads a line with all kinds of new line carriage return, new line, whatever, doesn't matter. Now you see this takes a buff argument, and this is the actual buffer that we need to, that we want to write to. And as it's read line, it reads into a string buffer. You can see here ampersand and MOT string. And this is where we meet mutability in Rust. We need to create this buffer, we call it buff. LatMoodBuff is string, and I get this new buffer, and how do I pass it in? And this is the second kind of reference that Rust has. Rust has a difference between mutable references and immutable ones. And both are not allowed to be active at the same time. You need to either mutable reference or immutably reference. You can also, if you have experience with concurrency safety, you can already see what this is going. Rust does not allow you to have, to look at data and to mutate it at the same time. And now that we have read this line, we return it. And the other thing that I need to do, I also need to mark the Buffered Reader as mutable because this Buffered Reader has a state inside the buffer. So reading, sorry, so reading will back to that buffer and we'll mutate it. If that one fails, currently we unwrap. We don't handle this, this returns a result. The question where the answer that Kevin gave, I mean to ask whether why we need to call this a second time. This is different. This is the variable as mutable and we're allowed to mutate it. And as Kevin says, this is correct. This is now we take a mutable reference to it. So for this to be allowed, it needs to be mutable but we need to state that we're taking this reference. This is sometimes seen, the question whether this can be inferred is a good one. It was actually investigated whether we want this to be inferred and the decision was actually not. So we want, especially at the place where we go from value to reference, we want to be visible. This is a discussion that has been had maybe in the future of this question. This discussion will be had again. Rust is a verbose language. I can definitely say as much and has no problem with that. Now, this is reading exactly one line from the input in a function. The next thing that we would like to do is apply our very simple parsing rules. So what we get back here, we get the line back and what we want next is figure out what the actual request is that we have. Ariane has the problem. I'm getting there a no method named ReadLine found for both reader reference TCP stream and current scope. Yes, that means you need to import a trait. I'm sorry that I've been jumping over there because the IDE did it automatically and I missed stating that you need use standard IO buff read there as well, which is the buffered reading interface. So buffered reader is a concrete implementation of buffered reading, but buffered reading is a general interface that's available. If you compile that one, if you would compile it on the command line, then the compiler would actually give you a list of traits that it knows that implement this function. The reason why you need to import it is for forward compatibility. So if we were to add, for example, another function of a same name in a different interface, importing it explicitly makes it even in the future, know which one you mean. That is a strategy that rust applies a lot to make sure that the code you write is actually compiling even in the face of interface changes in years out, unless someone deliberately breaks existing interface that obviously it doesn't work. So if read line but change it's type signature, it's function signature, then that would break. Okay, so let's apply our request parsing. Again, this is a very simple example for the sake of a tutorial. Calling this request parsing is probably stretching it, but the role of it is the same. And here I want to return a structured type. And I talked about these types that Rust has that express alternatives and I would actually like to define one myself. So what I would like to build is a type that says either you wanted to write something to our storage or you want to get something out. And I will define this type as a so-called enum, enumeration, call it request. And now what this gets is so-called variants and we have two variants, publish and retrieve. So I have publish and retrieve, but the nice thing here is that Rust allows us some of these variants to actually carry data. So I can say there's two kinds of requests, publish with a payload and retrieve, which doesn't carry one. As I'm here, I want to show a quick thing here which is so-called derives. Rust has a debug interface and multiple other interesting interfaces which can be automatically inserted by the compiler by you requesting them. So I can say derive debug and two others that I would like to have is EQ and partial EQ. This one being you can use the debug interpolation syntax, which is full on question mark with that type. So you can have structured output and these two are equality is actually defined. So request equals request can be automatically be applied. You can build custom implementations of these. So if you wanted to, you can omit the compiler doing that and just drag your own, but for convenience here and because they're so common, there's this derive interface that allows you to have the compiler auto generate that and the rule is very simple. If any of these here internally have data, here for example, string, then it expects that that string also implements debug, EQ and partial EQ and then the compiler can reason about the structure of the request type and say, okay, there's a trivial equality and debug implementation here. The partial EQ here is a little bit odd. You need that for floats, but EQ cannot be derived without a partial EQ being present. So I need both. And I think for the first couple of years of Rust development, the knowledge that EQ cannot be derived with that partial EQ is probably the one to follow, like I follow this just blindingly. Okay, so now we want to approach this request. And as I said, if it's empty, then I, then I consider this a retrieve request. If it's not empty, I consider this publish. The result of this is this request type. So how do we do this? First of all, I take the line and trim it and I use the trim end function. This is simple. This is just take all white space, new lines, carrier returns and spaces by the end and just remove them similar to a trim function in Ruby, Python, whatever. Rust in general has a pretty rich standard library interface that comes with all of these kind of functions. And now I can go and say if trimmed equals empty string, then yeah, I want to do one thing. And else it has not been an empty string, then it's well, publish. And the other one is the retrieve command. Now I need to construct these things and the way you construct these enums is you name the enum and then the variant. So this was retrieve and otherwise we use publish and now we want to publish the trim data and run into problem that this here is not a string. And that's due to Rust nature as a systems programming language because to avoid copying, if we have a stream in memory, a second, it's the classic problem with computers, they fail the moment you need them. So if we have data in memory, string, and we have a new line, what will be returned is referenced to a substring ABC. And due to the rules that I've laid out around ownership in Rust, we take ownership of the string buffer here. That means, and this rule is strict. I'm very sorry for those help things popping up everywhere. Let's see. So now that we took ownership, that means parts request will actually deallocate the string at the end of the function. And this rule is strict. So but I've been, but I'm handing out a reference here to the substring and I cannot do this because I would hand out a reference to something that isn't in memory anymore. Our quick fix for this is create a new string from that and remove this and think to note here, Rust is an expression-based language, similar to, for example, Ruby. So we don't need to say return request retrieve. We can, in the one arm here, return request retrieve. In the other arm of the if statement request publish. And that means the if statement always returns a request type and we can just let that flow out into, out of the function and we return a request. There's a question by Boban, which, whether to use string from trim to own or trim to string, there's a fourth form that also exists, which one to use in which case. I generally recommend, thank you. I generally recommend to use a string from, I like that the most, but they're actually all the same. They used to be a difference where two string was actually faster due to inadequate compiler optimizations that is gone since Rust 1.6. So use whatever feels best in the current situation. In this case, yeah, now we have this request. And now what we can do is we can, again, let those types help us in our program flow. And because we now have this enum type, you might've seen where this is going, enums get matched upon, match is like, you use match far more often in Rust than if and else. This is the standard way of doing any kind of a branching or decision-making. So now we can match the request here. And again, now have Rustana as a fill in the arms here. Now we can match on this request and now we can figure out, okay, was it published or was it retrieved? And what do we do? And this is, we published a message and here we retrieve a message. And for the sake of debugging, the first thing that I would like to do is leave in the line. So we can use this data type that we've used that expressed these two alternatives here to guide ourselves through our reaction to those requests. It's essentially if you look, this is a little bit of a rudder here. I would not use this, like I would not use match as a rudder for any kinds of systems. There's more, there's actually systems for that, but this is how you go through all those alternatives. And I would like to run this and it works. So if I go now and say telnet, you'll see another bad thing about witness telnet in a second is that it doesn't present the input that I'm writing and you can see here publishing message. And if I just hit enter once, it will say retrieving message. So sorry for not being able to present that visually. So now we have two of these things. Now the only, and you might have noticed, I've not been using the type system to let me flow through the things that I wanted to do. And I've only added this one data type where I introduced my own alternative, my own semantics here. There's a providence request and there's a retrieve request. The rest I've been flowing through the type system here. And now that I'm faced with this decision, now I have a problem. I actually want to store data and I have no storage. And on your question, it does actually take my input. It just doesn't echo it. So that's the annoying part here. So I'm gonna actually just go and pop this shell here off so that you see the actual reaction. Okay, so again, coming back to this, now I need storage. And the, this is again, something that I just pulled out of my knowledge. Which is the Rust standard library has a nice type called vector queue. It's a double ended queue based on backed by a vector. The nice thing around that is it allows me to push something in the back and pop it out from the front in difference to a normal vector type that Rust has that only allows me to push in the back and pop the last element. And I wanted to have this kind of first in, first out behavior. So pushing back, taking from the front is the semantics that I want. And how do we do a hand less this piece of storage? The problem is I cannot create it in handle client. Why? If we go back to this picture here, if I were to create the storage in here, so the networking layer calls or my main layer here calls into the handler. And if I were to create the queue here, I would doing every request actually destroy it again. So I need to make sure that it actually outlives handle client and lives longer than the call to handle client. So in some way, it needs to be passed in here. And as we've learned, we can take storage in a way that is mutable, but referenced. So it's owned by anyone who's outside of handle client. And yeah, so it's handled by anyone who is outside of the handle client function, but is willing to give me mutable access to it. The way we do this is ampersand. We take a mutable reference to a back to queue string. It's one being one that stores strings, which is our current message type. We kind of later actually introduce a proper structured message type if we want to the way it would be the same. This question Olga, is there not a lot of go challenge in Rust, not as a language feature? It's actually Rust did have in language channels at some point as a research prototype. There are channels that can be found in standard lip. Yeah, and as Sabrina mentioned, for example, the cross beam crate allows that. The nice thing about channels is that they also work by this ownership principle. You push something into, you send something through a channel, it takes it away from the sender and it takes and gives the ownership to the other side. So there's no chance to accidentally refer, for example, to send away data from the thing that has sent the data. Okay, so I need to create my data structure here. So I take let's start equals back to queue, call on new. This will yield an error because it doesn't know which type the back to queue is, but that will immediately go away if I actually pass the storage over here to handle client. It needs to be mutable. So I can actually hand it, give it away. And now on every loop doing every connection, I give mutable the storage over to handle client, can write something or read from it. And then on the next loop it will be passed to the next client and the next loop of iteration. And over here, that's simple. What do I do in publish? Storage.pushback, source that. But now what do we do in retrieve? And this is again a nice thing. Now I try to get a message out, but what if the queue is empty? So I may get a message or not. So let's try. The corresponding method is pop front. It gives me this data. And now let's have a look at what this variable is. And it is option string. And that says, well, it might be there or not. And again, this is an enum like the results or the one that I introduced. So these are the two things that may happen. Either there was nothing in the storage or you, well, or you can write it to the client if you want to. So let's match again. So if there was a message, we need to write it to the client. If there was no message, let's write no message. And here's where I need the stream again. Now I can say stream dot write all message. The stream takes bytes. So a string is not bytes, but it can be seen as such. And I use the as bytes methods to turn it into one. There's quite a bit to a wrap here in this line. First of all, Rust takes a very structured way on its lingo. So if I can interpret something as something else, the functions that do that are always called as underscore. So this is no copy or whatever involved. This is strings can be seen as bytes, not the other way around, bytes are not always you debate strings. Message as bytes. And you can basically just turn it into the different type. And then there's a write all function here, which you actually need to import a trade for, the write straight, which writes all of these out to the client. A slight warning here. Rust does not protect you from functional mistakes. There's also a write method. The write method will write as many bytes as it currently can until it will block. The write all will write all the bytes on the right side and wait this to be done. This is a frequent mistake here. People using write and then later if they write more data, actually losing data to the right because they forget writing the data. There's a problem here, which is the stream needs to be mutable so that I can actually write to it. There we go. Happy to oblige. There might be errors happening here, which the compiler will call me out on. It says there's a result being returned here, but you're not using it. You're not checking whether it actually works. And that actually being a warning and the compiler being annoying about, hey, you haven't checked whether that actually succeeded is a good thing, even though I'm choosing again here to just say unwrap, then let it crash. If that happens, then let it crash. But at least I'm seeing if the, like I will see it very visibly by my program crashing if that condition was met because now I am checking it. But my check is basically if the condition actually happens, then yeah, fail hard. And what do I do with there's nothing? I will send a small message to the user and I'll say no message available. There we go. Same thing may fail. And now I build and run this. So I turn that in again. They said, I won't show you a window that prints nothing. So just tell you verbally what I do. So if I send a message, it will be stored. I actually removed the logging statement. So I'm sorry for that. And if I connect again, but now I type nothing, then it will retrieve that message. And if I go again and type nothing, it will write me no message available. And again, I've been using the type system here to guide me by saying, oh, okay. So the two things that could happen if I pop from a collection is data could be there or data could not be there. And then react on that and let myself be guided by this type level flow where the type system can express this kind of branching behavior. These are the two things that you need to cover. Data has been there or not. In other languages that would often be abstracted to a null point or a null value. Rust doesn't have nil or null. It uses these kinds of option types here. And through compiler optimizations that doesn't actually cost me anything. So this is as costly as a null point to check here. In the back. For Albert, the stream, making the stream mutable is just annotating as mutable here. And for write-on, the trait that needs to be imported is the write trait, also from the AO module. So we're done here. On the question, on Adrian's question, why don't we need to specify the stream as mutable when we pass it in? Precisely because we have not used it in a mutable fashion at all before. So this happens because this is the first time we're writing to it. And yes, this is because of ownership. In Rust, there's the ownership principle basically says only one owner in the whole world, essentially of any piece of data. So, and this owner can actually decide, for example, to make immutable data mutable or mutable data immutable. So at this place, this is really just a choice and an annotation, which is, well, the compiler diagnostic tells you, well, it's not mutable, but you can add mood here and then it is. But this is a privilege that only owners have. I cannot not make an immutable reference, for example, mutable or things like that. So now coming back to this initial image, this is basically what we built. So we have this connection loop and we have this handle client. Question from Adrian, is it similar to const correctness? Yeah, a lot of similarities exist there. I generally recommend not to apply too many C++ principles to Rust directly. I give my case for it. Rust is much more, if C is here and C++ is here, Rust actually lands kind of over here and adds C++ generics in a way. Rust is a data structures and functions language, much like C and doesn't have a lot of concepts that C++ adds on top. But yeah, if it helps you as a first approach, yeah, these things can be seen simple. How do you get the documentation interface? That's a good question that maybe a Rust analyzer bug, Chris, so I'm not sure if I can quickly help with that. Sorry. Sometimes if you have both the Rust plugin and the Rust analyzer plugin installed, de-installing the Rust plugin may help. Oh, there's one thing that may help. If you have VS Code running for days, like I sometimes do, if there's a VS Code update in between the language server for a Rust analyzer will not install cleanly because that case cannot be properly handled. So sometimes like just quitting VS Code and starting it up again may help. Okay, there's something that I would like to spend the last half an hour on. And it's probably the most interesting part here is the problem that we now, well, not a problem, but something that we sometimes, that we observe here is we have this connection loop. But, so as part of the connection loop, we're calling into handle client and we're coming back here. And this is completely synchronous. So we're handling one client at a time. The kernel buffers that for us. So we're not losing any clients, but we're going one by one. And for that reason, well, we basically have no parallelism or concurrency problems because there is none. So one thing they would like to do for the last half an hour is actually take this and put it on a thread. The principles that I'm going to show here will be just as applicable to async systems in Rust as well. So for example, things like Tokyo or async stood for all those. So the safety mechanisms that Rust supplies are exactly the same, especially because all of those runtimes are always multi-threaded runtimes. So the problem of multi-threading is always the dominant one here. I just find it easier to explain that using threads because they're a little bit of a simple primitive that does not mean need understanding of the whole user space runtime that also comes with these kinds of things. But you can definitely take this example, take async stood or Tokyo and turn it into an async one using the types provided by those two and turn it into an async one in a matter of minutes. So, yeah. And now I want to be just very naive here and just wrap a thread around handle client. And this is where Rust shines. So Rust has a spawn function. Bear with me for a second for this syntax. There's a spawn function and the syntax here is simply, this is an anonymous function that is the actual function that will be executed on the thread. And once you're crossing the boundary to this function, move ownership. So it takes ownership of everything from the outset. This is copied from the standard thread spawn interface. The interesting here is that's a mistake and the compiler will catch that. And what it will use, yes, Tokyo's outside of scope for this workshop. But yes, as written Tokyo spawn and all of these things will just work similar and they have similar semantics, particularly around the moving. So for the sake of this workshop, it's completely fine if you replace thread by task and it will work, it will behave just the same and the principles, especially around moving ownership are the same. Okay, so the thing that it catches me doing here is, okay, I'm trying to move ownership of the data here into the thread because of this move annotation. And I'm going to show that on the console rather because it gives me a little bit more space. So it says you're moving this value into the closure and it knows that I'm looping here. So if I were to give this data away to the first thread, you will say, okay, so now if I iterate again, like what do I have to give away? The second iteration, the data is gone. So what to do, what to do? I could try to reference it and move the reference in. This will also not work because that will say, hey, we're trying to reference this mutably more than once at a time because I am not sure how long the thread is going to live, the thread that takes ownership. I will not allow you to pass a mutable reference into a thread which has like, because these things might start working in parallel or concurrently, independent of your runtime system. The whole concurrency and parallelism checking and Rust is completely independent. It doesn't know if you're running on a thread runtime which essentially your kernel is or if you're running on something like Tokyo. Rust doesn't know about this at all. It just knows, oh, so thread spawn will run thread spawn. I need to be aware that this is a component that has dynamic runtime behavior of each other. So you have two threads running in parallel and so you don't know how long this reference will be used in one. And Rust gives us two tools around this. And so we have two problems essentially. We have multiple threads or tasks or whatever. I'll just call this G1, G2, G3. We have these three and all of them want to have a share of the storage. They need some access here, but who should own it? So I have a problem. I have the single ownership principle that Rust has but I now have multiple parties that have interest in this resource. And the way we deal with that in Rust is a type called the ARC. Come to what that is in a second. Let me write it out. You need to import it from the standard sync module, standard sync ARC. And what ARC allows me to do, it allows me to have, like this just bigger item, I think that on the front anyways. The ARC is the atomic rough counter and the atomic stands for the counting as done in an atomic manner. So that I have no concurrency problems on counting them down count. And it's a reference counter in the sense that if I have three of those, what it will do, it will produce references to the storage, kind of allows me to reference the storage. But if I have three of them out, I will have a counter internally that says, okay plus three, so base three references currently out. And once those threads drop usage of this ARC, we will have references going down. And this will make sure that once this counter reaches zero, then it will actually deallocate the storage. And the way we use that is before we start threading here, I usually call that thread handle. I take the original storage ARC and call the ARC clone function on the original. And now I can use the thread handle over here. So now I have my second handle here. So what I have is I have my main thread. So I have my main thread and it will start, it will hold one reference here, choose the storage reference. And every time it spawns another one, before it does so, it will create a second one and gives the thread access through that. So this handle-based management allows me to count how long I actually need to keep the storage around, which because this handle will be dropped after the connection loop is basically forever as long as the server runs. So I always have one available here. Okay, now we have a little bit of error message earlier I think, accidentally, but now we have another problem. I've always been talking about this problem that it just does not allow you to have multiple mutable references out at the same time. And for that reason, if I have this kind of setup here, these ones are not allowed to produce me mutable references because if you're thinking about it, we have these multiple parallel threats happening at the same time or tasks assigned to feds. And that means we don't know if they would not, for example, take out, try to mutate the shared data at similar times. So if I draw a line here, there could be moments where these actions overlap. And this is how Rust guarantees concurrency safety actually through the definition that mutable references are required to write to memory and that mutable references need to be unique. Oh, sorry. Thank you. Thank you for the pointer in chat that you couldn't see the drawing. So if we have these threats taking out mutable references at the same time, there could be a moment where multiple mutable references are available at the same time. ARCs have no similarity to go context weight groups. This is deep as my understanding for go context and weight goes. ARCs essentially just use the processor feature of atomic additions and there. There. There's a quick question on why I use our clone here. Purely out of habit. There's a debate which one is better. You can also use just as an illustration. You could also use storage.clone. At this moment, I think it makes it more visible that I'm cloning this ARC handle basically my lease on a ref counted value while the clone interface is the general cloning interface that Rust has, which also allows you to feed this in, for example, in things that want to express multiple ownership. Feed this into things that might want to clone and that would cost you, that would cost you, for example, a lot of memory if you were actually doing this. So this allows you to do Jeep cloning. Okay, so let's get to this mutable thing here. So what I essentially need is something that makes sure that if you have multiple threads and they want mutable or even immutable if they want to read synchronized access to a value here, we need some type that moderates that. And the classic type to use here is in UDX. And I would like to use one here. In UDX is in the standard sync module as well. And it works just the same here. You use it, you construct it around this vector queue. And what we've now done, let me put the drawing on the screen first, take this down a little. And just for those that were asking around Tokyo, Tokyo uses ARC from the standard clip, but for example, provides its own ASync Mutex. And that's the, there's two things you would need to replace to run this on Tokyo, which is replacing thread spawn by Tokyo spawn. And by replacing the Mutex implementation by an ASync Mutex implementation that interacts with the runtime. But the exact same principle and the exact same API applies with one minor difference that is seen as an API mistake in the standard clip. But you can really go like import Tokyo and run through this whole program and change it to an ASync fashion. So now we have this Mutex wrapping around the storage and the ARC now gives me access to the Mutex. There's an interesting thing that now happens in the API. We need to take a Mutex here because we want to interact with the Mutex. Standard sync also provides you with other kinds of primitives. If you have different kinds of read and write behavior, like you can, for example, an RW lock or things like that are better for you. You can also find that there. But for the sake of it, I'll be using the Mutex. So now I have Mutex VectQ string and I come back to this piece of signature in a second. But now, first of all, I want to use it. So I cannot call pushback on the storage here because it's not a VectQ anymore. The way we end up doing handling this is we want to lock. So we can request one. This is the one location. Maybe I can go into details later on why that is, but this is why I've been hinting at with generally seen as an API mistake in the standard lip. Locking returns a result. And this result is almost not handleable in the case where it actually fails. So this is a point where we all call just unwrap and just be, okay, with that. So this is really, like, if this result fails, your program is so broken that you probably cannot recover and then panicking and quitting is the right thing to do. And what this returns is a so-called guard. I call this guard. This is a value that represents I'm currently holding lock and I can use the guard to actually write to the data because this represents I'm holding a lock and it knows I'm holding a lock to a VectQ and will expose the function pushback. Can I go into the exact mechanics on how this works here? But because I would like to talk a little bit about how that guard works in a second, but just before we're doing that, I would also like to again do that here for retrieve. Yep, this is exactly what I want to talk about before when I talk about the guard. Thanks for the question. So when is that lock released? We need to mutate it because we want to mutate through it. And this guard represents, okay, you're currently holding the lock. Now you are allowed. So here's the guard. And because the mutics internally guarantees that this guard can only exist once, that's the definition of the mutics, this one will finally produce you a mutable reference to which you can write. That only exists once. This is the thing that a mutics provides. So this is like the cleverness that Rust has there. So there's the question, when is the guard released? The guard is released by the end of the scope. Yep, so when it's dropped. That means it's a slight warning here. I've been using, I've been binding the guard explicitly here. Here, that means the guard will be released after actually writing to the client. So I'm actually holding it for a little too long. Which is why general practice is actually to do that all in line. That means the guard will only be held for actually this line of code. So if you don't bind things to a variable, they will be temporary and immediately dropped. And that makes sure that this year works. So this is actually a good practice when I'm working with mutics, yeah. So what we see here is we have the mutics and then we have this way to come back to mutability by working through the lock interface. And there's often the question whether that's an escape hatch in the language. It actually isn't because I said, I wanted to come back to this type signature and make sure that you understand it. This says here, you get a reference to mutics. You are not owning it. The mutics wraps a vector queue inside. And the mutics actually owns it. So you have this kind of picture here, mutics and really the storage on the inside. And the immutable reference here says, you are not allowed to manipulate the mutics. You're also not the owner of the mutics. So you're also not allowed to destroy it or anything else. You can only work through an interface. You are also not to access what's on the inside. The only way to actually get to the inside is this locking function. And this does three things. So here the mutics exists before I'm actually calling here. The moment I'm locking here, the guard starts existing. Sorry. And the guard exists until here. But in between, there's a third thing happening. It happens when I call push back is at some point there will be a mutable reference to this vector queue be available. And this one is enforced. And this is what you generally know as borrow checking if you like hear about that at some point is enforced to never live longer than the guard. That would be a violation. So Rust does three levels of safety checking here. First of all, to make sure that you don't start like manipulating the mutic structure raw and that it's properly initialized and this is a properly initialized mutics that before you are even allowed to have a reference to the inside you have made sure that there's a lock. But the third thing is that it will also make sure through this kind of layer checking it will also make sure that even after you release the lock you are also, before releasing the lock it's better to say it that way. Before releasing the lock, you also gave up any attempt to manipulate the data. So if I were trying to manipulate the data after dropping the guard, we can't use this case. If we wanted to, we could just force. That's, this is not, this is not very meaningful code. This is we lock, we guard and then we try to write to it. So we lock, we get the guard then we throw it away and then we try to write to it. This is not something one would write. But if you were to run this, it would say, wait a second. This is the wrong error message. This is the one I wanted. If you were to look at this, then it would say, hey, you are trying to write to this card although you have thrown it away. And Rust is not extremely magical around this in the sense that, well, what it does is draw these regions. Sorry, they should, what it does is draw these regions and make sure they follow a hierarchy. And that's what's generally known as borrow checking, making sure that what I have, that my loan on the VectiQ does not outlive the time that is actually valid. Okay, are there any questions around that? Ah, should maybe run it. On Yannick's question, are data race conditions possible? No, that's exactly the thing that Rust spans. Data races are not possible. Lock races are just to be clear. So, but data races are not because data races, like Rust is a memory, basically follows from Rust is a memory safe language and data races are a kind of memory on safety. Yeah, that lock on wrap pop front will immediately release the lock in that line because it's temporary. The mathematical proof for this is called Rust Belt. So, if you look at the University of Sebregan, Rust Belt Project, that is, you will find it there. Yes, you can deadlock and lifelock in Rust. You can, there are ways on how you can force a resource acquisition order if you structure your types right, but that is something you need to work for that doesn't come naturally out of the language. You can use the type system a little bit towards that. The questions on CC++ and UNDelete in any way, no, there's nothing similar to new UNDelete in the way that you will be used to in CC++. We, like Rust doesn't even have things like special constructors. There's ways to create data, but the destruction of data always goes through this drop interface. You can write destructors. That's the thing that I haven't shown, like all of these things work through destructors, okay. And I will quickly share this code already as a playground here. Rust has the Rust Playground. I will show that on screen for a second. So that is our online playground to probably know and it has this nice share button and I will copy the link here to this. That's the code that we've written and can start playing around for that. On Thomas question, the art no longer is mute, is that because of interior mutability? And yes, we are, you've seen this warning here, mood storage does not need to be mutable anymore. Rust has this idea of interior mutability just quickly, which is precisely this here where I have immutable access to this but in some way I can produce mutable access here. And I said like, I cannot, like, if I have an immutable reference, I cannot get a mutable reference. And this is in general true in practice that would make mutics is not very useful types because that would mean I would have to need mutable access to the mutics already to then produce a mutable access to its inner parts. So there's this concept of inner mutability in that types in and by themselves can say, I will manage mutability by myself, but and particularly at runtime because that's what a mutex does is a mutex like it's the moderator of mutation essentially. But what that means is that it still needs to uphold this guarantee that mutable references in Rust, if there is one out, there's exactly one out. There's also no immutable references. There's exactly one mutable reference to the vector queue available at that moment in the program and nothing else. And that's important to make sure that this is true at all times. And mutics is especially many concurrency primitives do use this kind of feature. Yeah, the compiler knows about this, it knows, okay, these are types that manage this kind of mutability checking on the inside by themselves. But the language will still exist. So if I have a function here that says, now they're all gone. Oh, if I have a function here that says, I take a mutable reference, it still needs to make sure that in all cases, while buffered reader read line works on this specific buffer, it knows this is the only execution of this function on this specific piece of data. And again, coming back to what I said before, this is the crucial part of the concurrency and safety checking of Rust. And if you take this piece of code, I can highly recommend doing that as an exercise. If you take this piece of code and then have a look at the Tokyo API, you will see Tokyo also has TCP stream. It has buffered reader. It has TCP listener under the exact same interface. It has mutics under the same interface. It has a task spawn module instead of a threat spawn module. And this is the most important difference behind this, but the safety checking works very much the same. And the construction of this program works maybe much the same, which is an interesting experience to see where Rust takes the common ground between all of these kinds of things and makes it safe. And so you can use, and this is also why I picked going down that route for this exercise, because it gives you an access on how the mechanics work. And then you can apply these mechanics to just Tokyo or whatever right away. So the Tokyo task spawn interface, if you look at it, is very, very similar to the threat spawn interface. Going back to that point of common ground. So, yeah. Are there any other collections themselves threat safe? No. So standard lip collections and Rust are not by themselves threat safe. And you would need to use these kinds of things, but Rust knows that they're not threat safe. So they are safe to use. And this is exactly what it has figured out when I was trying to pass. If it were threat safe, it would not have complained the moment where I'm passing it over through an arc and would try to mutate it in a way. There are though implementations of threat safe collections out there as crates. Happy to. Last round of questions. It's always good when someone says eye opening. I have an async implementation of this, sorry, online, that is a little bit more elaborate. I will, I'll have so much time to actually find that link that takes a little bit more of a more complex protocol that looks a lot like the Redis protocol. So it also has a library as a protocol poser. It's written using async std, but given that the interface of async std and Tokyo have moves very closely to each other, you can basically just go and remove async std, replace it by Tokyo and use it as Tokyo. And greetings to Germany. Yeah, I hope I give you a little bit of an overview of the basics of what make Rust teasing. And the last thing that I would like to give you on your learning exercises, ownership is the important principle in Rust. If you are struggling with the Borotech or a little reference less, use more clones, use ARC is like use things that like use own types that makes Rust a lot easier. And there's an easy refactoring for if you figure out, I'm copying data too much and all of these kinds of things. And then actually start referencing more is a good way to learn the language. So ownership is a thing that you should focus on first. Oh, and because it's a quick to answer, Frank. Yeah, that was the thing that I had hinted at. There's a type in Rust that is instead of mutex, it's called RWLock, which works the same as mutex. It wraps the data, but it has two functions, one read and one write, where you request the kind of locking that you need. Okay. Thank you all for attending the session. Seen more than a hundred listeners and number has dropped. So thank you. And so soon, if you have any further questions I'll be in Slack, please feel free to ping me. I may need some time to answer. But I'll be going through all the questions you sent me. Bye.