 Welcome everybody and I'm very happy that you made it here, especially considering that it's 9.30 in the morning on a Sunday and maybe a few of you have been drinking yesterday, so I appreciate it. Also the people that are on the live stream, kudos to you. I hope I don't disappoint. What I want to talk to you about is idiomatic rust and it's a huge topic. I want to show you a little bit on what I learned in my journey using rust in the last couple years and how my code evolved over time. But first, who am I? I'm Matthias. I live in Dusseldorf. I'm a backend engineer at Trivago. I do a lot with website performance, monitoring, metrics, talk to me about it. I do like programming languages a lot, so I'm kind of a language nerd. And I also like hot chocolate. You can find me on various places on the internet, so on Twitter and I also have a website. And now your expectations are somewhere here for this talk. But rust is a big language and it's getting bigger every day, so I cannot possibly cover everything in 20 minutes. That's insane. So lower your expectations a little bit. This is going to be something more practical, something more hands on. You might be saying, okay, why should I even care? Why should I watch this talk? Well when I started programming, I was attracted by languages that were simple, beautiful, elegant, that had a concise syntax. And I had a favorite language, of course, and by now you might have guessed which language that is. Of course it's Python. But I did a lot of Python in the past and I appreciated what they call the send of Python, which is those rules, here are the rules. I hope you read them all. They are very important. There will be a test after the talk. Now those rules, they are more or less the gist of what it means to write good, well-formed Python code, Python code, that's even a word. We're not going to talk about Python anymore, but we want to talk a little bit about rust. What is idiomatic rust? And for that you need to know what is idiomatic. I hope you've seen the previous talk from Antonin, which was super awesome. You should check it out, if not. So I will focus on the idiomatic part here. And there is a definition which I quite like, which is the most concise, convenient and common way of accomplishing a task in a programming language. That's from Tim Mansfield, that's from not from me. All of those things on the line are linked, so you can check them out later. So that's quite awesome, this description, because yeah, it's concise, convenient and common, so that makes it idiomatic. And not in any programming language, but in a programming language you're writing. It should feel natural in the language you're writing. So if you're a Java developer, this might be idiomatic Java. I'm just kidding, of course, I'm just kidding. This is non-idiomatic code. You can see here that this code will work. It's checking for a Boolean value, but in a kind of awkward way. If you like those kind of things, there's also a link down at the bottom for more of those. But everybody can agree that this is not really idiomatic code, right? Most people will say, yeah, that doesn't feel right. But what is really good code, what is solid ergonomic natural code? You can focus on many things, but if most people would say, yeah, you could focus around those areas, syntax, semantics and design patterns. And since this is a small talk, I'm not going to talk about syntax or design patterns. I'm talking about semantics. For syntax, just use a standard, any standard, can be rust format. Actually, use rust format, it's quite nice. For design patterns, there's a project from one of the core members, Nick R. Cameron, rust unofficial patterns, do check it out. But for semantics, I did not know where to start. And I was looking for a lot of resources and I couldn't find them. When I started, there was literally none. So I had an idea. What if I started collecting a list of things where intermediate Rust developers could go and read about more sophisticated code? So there are a few links from projects and talks. Actually, a lot of people that contributed are here. So, yeah, ask them if they know something about it. Please contribute to that. I guess this will be a first step towards something that is a guideline. Not a scent of Python because we are not Python. We don't want to be Python, we want to be something like rust. But a start of a conversation on how to write idiomatic rust code. So if you find something on the web and you say this is missing on the list, shoot me a poor request, I'm going to transfer that eventually to some official space so that you are not locked in or something, I would happily accept your recommendations for that. That is all very nice and stuff, but it's also very theoretical. So why not look at a very practical example? And for that, I thought we build a little tool, a little library. And the case study is going to be handling money and rust. So we think about a data object, how does a data object look like and how does the compiler help us? Guiding us along towards something that is more convenient to use and feels very natural. So the task is, parse money, what can go wrong? Let's parse some money, so $20.42 or 140 euros. This is our task for the next couple minutes. As a beginning rust programmer, you might start with a function like this. You take an input which is a str, which is borrowed string, which lives on the stack, and you do something with it. And this something might go a little bit like this. First you need to decide what you want to return. So you could say, yeah, I don't know, in my previous language I use a tuple for that. I say my monetary value is an i32, so an integer, and the currency is a string. So I take the input, I split it on the white space, and I collect that into a vector. So this parts thing in the end has two components if everything was right. One is the monetary value or the amount, and the other one is the currency. We check if we have an amount. We just parse the first element in our vector. And if we have an error, where we return some magic number, as we do in C, or many other languages, and we say we have this invalid currency. And if not, then everything was all right. And we just take the second element from our vector, which contains the currency. We parse it into a string and we return that as a tuple. Yeah, can work, not too beautiful. Why? Well, let's look a little bit about what I'm doing here. I have this explicit error check. So I'm checking, I have a condition, so I'm branching. I need to have more mental overhead to remember that there can be something wrong, and also I have this magic number minus one. Which you need to remember is not a correct value. And minus one is a totally correct value for a currency, for a monetary value. And invalid, yeah, somebody invents a currency called invalid? Well, bad luck for us. But nevertheless, most people in the community might agree that this is not the way you write solid Rost code. So the first thing you can do in a prototype is to replace this error checking with UNREP. A lot of people will say UNREP is evil. I don't think so. UNREP can be very idiomatic, especially if you're writing a prototype. And if you want to get something done real quick, then UNREP can help you. Because later on, actually what is UNREP? UNREP will just try and fail. And there will be a panic if there was a failure. So you might say, whoa, this is dangerous, and it is. But also it helps you get going. You don't need to think about the error handling part in the beginning. So that's not bad. It's just a pattern that a lot of people use. And you will see why. So if we take this library, actually this function parses our thing. We can use it to parse our money. And we're done, right? Well, what if somebody tries to parse some sub-euro amount? Well, bad luck. You get a compile error, actually a runtime error with a panic. Even though Rust is safe, it cannot save you from not declaring what you want. So our intent, our intent, we must define. Actually, this is my colleague's cat. I think it's very ugly, but that's just a side note. It should be a meme somewhere. So we get an error saying result UNREP on error. And then we have this weird parse and error invalid digit and so on. What happened? Well, turns out we panic on this UNREP here. I told you, well, it's totally fine to use that. And the reason why is that you can later, when you have something running, search for UNREP in your code and just replace it with something nicer and have error handling out of the box. So you have the best of both worlds, rapid prototyping and safety when you need it. So one common pattern to use is you replace that UNREP with something we call the question mark operator or the carrier operator. That's in line number three behind the parse. We just replaced UNREP with question mark. Let me go back one second. You see UNREP becomes a question mark and then we return a result. So in the end, in line number five, you see okay, that is the correct result. So when everything went right, the happy path. And if there's an error or so, then for example, in line number three, the question mark means there can be an error. So in this case, we just return a parse into error. You can see in line number one at the end, there's the parse into error now. So we just pass on the error to the caller and we don't deal with error handling anymore. It's also a better concept than exceptions for us because these are zero cost. You don't have a runtime overhead with that but it's very concise in the way you want to write your code and in the way of error conditions that can happen. So all of that is covered now. And by covered, I mean, if you take the same example from before, at least we don't panic anymore. So, but we get something which is looking a bit foreign or a bit alien. You get an error, parse into error of kind invalid digit. So parse into error is in fact an enum and enum has, actually parse into error has a kind field. And kind can be many different types of error, kinds. And for a Rust developer, that totally makes sense. But maybe not for a non-Rust developer or somebody using your library. But first, we're trying to parse a float here, right? And we get a parse into error. So we use the wrong type for the parsing. If we want to parse a float, maybe use a float. There might be people here in the audience which have a little experience on how to handle money. Float is not a good idea. Don't use float in production code. This is not about proper business logic. This is about Rust. So that's why in this talk it's fine. But for production use, please check out the link at the bottom, which you totally cannot read anymore. Like I see faces stretching up. But there is a link at the bottom saying caution, please don't use float for real-world money object. In this case, I want to improve the error handling here. So think about the Rust part. But if you're a business person, it's totally fine if you don't accept that solution. Let's go back again. We have that float32 and we return the parse float error now. So with that, when we parse that thing, yeah, we actually get the first real result, which is our tuple of float and then a currency. What about this one? Okay, we are missing a currency. That's an error variant because we can hardly know what currency that is. We don't. And what happens if we run the code? Well, we burn because we die at runtime. Again, because it says length is one, but index is one. And why? Actually, let me show that cat again, just for a good measure. I forget that I had it in the slides. This, we parse the first element. We try to get the first element from our vector, which is undefined. We should handle that. We can handle that explicitly and say, if the length of our vector is not two, then we don't have an amount and a currency. So then we need to return an error. You can see that I don't write return anymore because return is implicit. Everything in Rust is an expression and the last statement will be returned or actually the result of the expression will be returned to the caller. In this case, in line number four, that's a return. And in line number seven, that's also return. So you can see that we do this explicit check for two elements. And also you can see that we have a new type here, a custom error, which we call the money error. And in this case, the variant of that is a parse error. You define it like this. You have a money error, which can be any type in Rust, so like an enum with one variant, which is parse error. You need to implement error for this type. What you get from that is when you try to print it, this is what will be printed. So that's just a trait that we implement. Error is a trait and description is a method that we need to implement, which belongs to the trait. And it's also the only method in the trait. And when we implement this, more or less we have an error type. At the top you can see debug, derive debug. That is some kind of standard way to have something, some output when you try to print it, because there's no real, yeah, actually you can provide a default output for that. And this is handled with the derive. And for our description, this is handled when we try to print something that the user should understand. Also, we need to implement the display trait. That's a lot of boilerplate, you might say. You have this display trait, you have the error trait. You even have another thing for converting from our parse float error to our internal money error. So we wrap around the internal standard library parse float error, and we get something that is our money error. This is a lot of code, especially for a beginner. So there's a library to make that easier, which is called failure. And the same code that you see here can also be expressed like this. Even, there's even more in that slide. Actually, we have two error types here. We have parse amount error and we have parse formatting error. Parse amount for, yeah, parsing the amount of course, and parse formatting if we don't have two elements. So we can separate the error types here. And yeah, we keep the implementation for parse float error down there to do the implicit conversion automatically. With that, you get beautiful error messages. So you say, if you're trying to parse something without a currency, you get expecting amount in currency. If you parse something which is not digits in the beginning, you get parse float error kind invalid, all of our own types. And the usual suspects for the normal case. But still we check for the length here explicitly. And yeah, that's kind of, yeah, there might be a better way. And there is using slice patterns. With slice patterns, you can say, I take this slice in line number four. I take the vector and I take all of it. So that's why we have two dots here. We take the whole thing and we compare it to different outcomes. So match means compare it to different versions, to different outcomes of the computation. And we compare if we get an amount in the currency, if so we return it. And if not, then yeah, we throw an error. But this is just in Knightly right now. But this will help you later to make your code even more readable. So now we use our own money type also, instead of returning a tuple. There's also, the way to implement it is you use a struct money. You have two fields, amount and currency. And this input money block down there, this is where you write your methods. And yeah, we just have a new function which returns a new object of money. And for the currency, we also use a currency enum. And we have dollar and euro as types. And we can also implement from string, which is another trait for the currency. And we only need to implement one method, which is called from string. And in there we do the matching of the string input, so dollar or euro, and the output of this function. So when we call powers on a string, which is euro or dollar, then this will return an object of money. An object of type currency, of this enum. And the same can be done for our money type. We also implement from string on that money type. You can see that I just copied over the function that we had before. In the very beginning, we had our parsing function. I just copied it over from line five to line 13, I guess, or 12. Just took that block. And now, if I have a string, which is a real monetary amount, and I call powers on it, then I can return automatically an element of type money, an object of type money. That is super helpful, because then we can do things like this. You just call it on a string, and you see that colon, colon, turbo fish, colon, colon, angle bracket is called a turbo fish. And inside, I define the type that I want. And with this, yeah, I get something which is very fluent to read. I say 100 euro dot parse, colon, colon, angle bracket money, colon, angle bracket. And that's it. So we took our initial code, we modified it based on the compiler's input, and then we reached something which is both readable, maintainable, and safe. Without doing much. So this is what I learned as a process in those last years using Rust. Start with something iterate, and the compiler will help you a lot. So if you're interested in that kind of stuff, check out my website and also ping me on Twitter. And please contribute to that list. Maybe we can make it bigger. And if you want somebody that can guide you along, then use the clippy crate, which has a lot more lints, to help you write more idiomatic code. Thank you. The question was, since you cannot use the slice match because it is a nightly, what would you use in stable Rust? Well, in stable Rust you can use what I showed you in the previous slide, which is just the explicit matching on the type. What would also be nice in the future would be collecting into a tuple because we collected into a vector, and then we checked the elements of the vector, but this is not possible right now. There will be a lot of those paper cuts will go away in the next year or two because there's an ergonomics initiative. And that means that all of those things, they are right on the verge. So you can expect this to become stable very soon, I guess within this year. And then you will see the Rust of next year probably will have even more patterns. And this is like a growing ecosystem and a growing stack. So probably this list of idiomatic things, this will be longer and longer and longer. And the things that I told you today might not be the things that you want to use in 10 years. But same with other languages. There are some paper cuts I admit, but we will get there in the end.