Rust Programming: Moving Beyond “Hello World”

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hi everybody i'm ryan um i'm excited to be here um who am i i'm a cloud developer advocate at microsoft and my day to day is full of all things rust and today that's exactly what we're going to be talking about we're going to be talking about the rust programming language and what it is how to do it and we're going to go a little bit beyond hello world so i'm going to assume a little bit that you know what rust is and what it's all about but feel free to ask any and all questions surrounding that um but we're gonna blow past uh what rust is kind of at a high level and why why is there another programming language named rust we're gonna dive right into the code and and by the end of the hour and a half or so that we have together today hopefully we have a little bit of an application written in rust that works together so i'm going to pop on over to this view here hopefully everybody can see that fine and we're going to be talking about rust so of course you want to have it installed on your machine the best way to do that is to go up to rustup.rs which is where you can get rust up and rust up is rest up is a a program for managing your rust tool chain so there are various versions of the compiler there are different tool chains that you can use there are different kind of core tooling that you can use and rust up um is a great way uh to manage all of that um you can think of it if you're coming from the node world like an nvm or something like that where it's managing all of your your different versions of rust as far as what code editor you should use you really can use anything the thing that i use that works really well for me is vs code with the rust analyzer plugin so if you uh install vs code and install the rust analyzer plug-in extension for vs code then you should be on your way but it should be fine to use whatever you want emacs them um you know sublime atom i can keep on aiming them all that anything should be really um you know should be usable with rust so without further ado i think we should just go ahead and get started and start writing a little bit of code hopefully that sounds good to everybody so i'm just going to hide that real quick and i'm going to be using wsl on windows today of course you could use it on native windows if you want to you can use it on linux you can use on a mac os and everything that we're we're doing today should be roughly the same i don't think we'll actually have anything different between any operating system but if we do come across that i'll make sure to give a shout out there so once you've installed uh rust up you should be able to to see this here and rust up by default installs both the rust compiler rust c and cargo which is the rust built system for your uh specific architecture so if you're running like i am on x86 64 windows it will have or linux in this case it will have that compiler and that cargo build tool installed on your machine and just to make sure that everything is working fine you can go in and just type cargo and as long as you're seeing something like this russ package manager here then you should be good to go and of course if you have any troubles um you know shout out and chat and we'll see if we can get some help for you there all right so what are we actually going to be building today well i think the best thing for us to build uh would be a key value store command line utility that's going to really exercise our rust skills without pushing them too hard and and should be doable uh in about an hour and a half that we have um and so what that's going to allow us to do is to get and set um key value data from the command line where we will be able to type something like um if we if we call our our utility kv store um we could do something like kb store you know hello and world uh like this and kb store is not found and then we could do something like kb store hello and it should return world back to us so that's just a little kind of key value story utility uh hello nature hermit there what did you miss we're programming some some rust today so that's what we're doing and we're working on going to work on a key value store that's going to be our little command line utility that we're going to build today we're just getting started so you haven't missed too much all right so how do we get started with a um an actual uh utility a binary executable and rust well we'll go ahead and just do cargo new and then the name of the project that we want to do and you know i've decided to call this kb store like this and you'll see that cargo has created a new package for us called kb store and so we can go into our kb store just like this and do i have tree i don't have tree installed on here if we look at the the actual layout on our file system we have a cargo.tamil file that's a kind of a metadata file that describes our package and of course we have a source file in here and that just has our main.rs file that's where our code lives and there's really nothing much more than that so cargo does a little bit to help us out here but not very much we don't have too much going on here so i'm going to pop this open in vs code and you'll see we also have again ignore file that cargo has created for us which is really helpful and now my rust analyzer plugin has kind of picked up here and created this cargo.lock file which is a a dependency lock file um and a target folder which contains all the build um artifacts that we're creating here and they're ignored by default um so this is just where our our end executable will end up in this target file here but that's not important the important part is we have hello world in here so and it looks like rust rust analyzer is bugging out on me that's fine if we look uh here um we simply come with a kind of default um main function here that just prints hello world to standard output so just to make sure that things are are working and in fact i'll just do this real quick uh in here um i'm just gonna run cargo and run and that will compile and then run our executable and you can see that it prints hello world uh to standard output so everything is working here cargo run is a really useful tool and there's going to be a couple others that will use cargo build is just going to build the executable not run it and cargo check like this will go ahead and type check but not actually build the executable which saves a little bit of time because generating the code takes takes some time there's a question in chat is cargo part of rust so cargo is is not part of rust it is a separate thing from the rust compiler but it is a tool that is very highly integrated into any rust programmer's workflow and i would say 99.99 of rust developers out there are using cargo for their for their development practices and and so really you can think of cargo as a build tool a dependency manager um and and a a kind of a project bootstrapper and so it has gone ahead and bootstrapped a project for us but we could have done this on our own if we wanted to um and it does auto-generate this hello world for us just to just so that we get started um and we're being asked if we can increase the code size i'm going to go ahead and do that real quick and then collapse this over here and hopefully that's a little bit better let me let me know if that works for you and if you want to know how to open up the terminal from vs code um on on my machine at least it's just control and tilde opens it up um all right so uh without further ado let's actually start writing some code so what do we need to do here well the first thing that we need to do is probably parse some command line arguments remember again we want to be able to write something like kv i'll just blow this up a little bit more oh sorry we want to be able to write here kv store hello world to store hello the key hello with the world um value in our in our database that we're going to create and so of course these are some command line arguments um and if you're used to programming c for instance you would expect your main function to take in arc v and arc c that we don't have that in rust instead what we can use is the standard oh and i really hope that uh i don't have to turn off rust analyzer i'm not sure if my latest rust analyzer because i was messing with it um ends up ended up actually breaking something here we'll we'll see in just a second um yeah we'll have to turn it off that's unfortunate that's okay though let's go ahead down here and we can go ahead and just disable this real quick all right so we're going to call standard inf and then arcs and this will go ahead and get us the an iterator over arguments and so we're getting we're already getting into iterators and rusts this is this is already somewhat of an advanced topic but that's fine because all you have to know about iterators and rust is it allows it allows you to continually get new values by calling a next function so you call next on it and we'll give you another value you call next again it will give you the next value until it doesn't give you any more values and the values that it's going to give us here are the arguments um in on the command line that we want and so um if you've done any uh you know programming on unix before you know that your command line arguments the first argument is going to be the actual execution the path to the executable itself which we don't really care about and the great thing about iterators is that they come with all these high level functions that allow us to manipulate our our iteration and one of them is skip so we're going to call the skip method on our iterator here and that allows us to skip one the first element we're going to skip one element and then what we can do is just call this args here so this is going to be our args with the first element skipped and coming down here again we can just go ahead and run cargo check and you'll notice we're getting a warning here saying we're not used using args but it but it has compiled fine so we've run cargo check here and we've everything is running fine we're getting a warning saying hey you're not using args um which is true but that's that's fine a warning is is something we'll take care of in just a second any questions about this chat let me know this is the basis of our program so far okay so now we have the arguments what do we actually want to do with them well we probably want to go ahead and get our key and our value and well the way that we can do that real easily is by saying hey the key is going to be the very next thing in our iterator so our iterator is args and we can call next on it like this and just again just to make sure we're compiling up we've got an error here here's our first interesting uh error that we have we're getting an error saying we cannot borrow args as mutable because it's not declared as mutable and here's a key to learning rust if you've not done any rust programming before the absolute key is to read the error messages because the error messages in russ are actually very good and they'll usually give you the answer to what you need to to do here and so help consider changing this to be mutable so in fact args when we're iterating on it we're actually mutating the iterator because the iterator is keeping track of where we are in the iteration so we need to actually mutate it and by default rust is completely immutable so we have to opt into mutation and rust that's very different than a lot of other languages that are the opposite where everything is mutable by default and you opt into immutability rust is the opposite and i i must say i find this to be a much better uh default immutability is a very good default to have so we declare that our args can be mutated and if we go ahead and check this again then we still have a warning here that we're not using key but everything has compiled just fine um so uh there's a question in chat does next item what happens if the next item is null so that's a very good question um and we can actually uh check on this and i'm i'm a bit sad that my my rust analyzer is that i broke my rust analyzer here i don't know what happened here why it's bugging out on me it's a bit of a shame um i'm wondering if i go ahead oops just real quick um if i uninstall and reinstall maybe that uh maybe that will help here let's see here oh seems to seems to have temporarily helped um because we have some we have some uh some helpers here this right here you see just showed up now this is our this is i didn't write skip args here at all this is rust analyzer showing us the type that args is so so rust is a statically typed language and that means that everything has a type and so we can gain some information from the type there was a question in chat about what happens if next is null like what happens if uh when we call next there is no other argument like let's say we pass uh we don't pass any arguments to the command line well you'll notice here that the key is an option string so in rust there is no null um everything in rust you know you can't have null and rust at all there's no concept i shouldn't say there's no concept of null null is a very advanced concept in rust that we will not be touching at all today in general you don't deal with null at all you deal with optional values so if you're coming from a language like swift this will be familiar to you or or haskell or some functional programming languages you'll notice here that when we call next we get an optional string so we don't get a string back that might be null that's you know that's not very helpful we know when we have a string we have a string but here when we call next we don't know if we have a string because we know there might not be another command argument so that's why it's an option here and this lets us this forces us to handle the case where we might not actually have the next string all right so if we go ahead and uh let's just say uh and rest analyzer i don't know what's going on why it's why it's being sat at me so i'm just going to go ahead and disable it for now that's a that is a pity all right so we have next year i don't know i what is going on i have oh reload was required sorry for that okay so we've turned off for us analyzer again which is a bit of a shame but so what about the case where we don't care if there is not another value we really just want to like crash the program if it's not there well russ comes with a nice little shorthand for that we can just call unwrap here and unwrap we'll crash the program if the next value was not if the value from next was not there if there was no value also known as none and if it is there then it will return back a string here so the value we have in here is going to be a string otherwise we've crashed the program unwrap is really great if you're just kind of writing a simple program and you you want to get you know you don't care if to handle errors or something like that you just want to crash the program if something goes wrong later on we'll see how to handle these errors more more elegantly than this and the great thing is now we can do the same thing for our value so if we go here and say args next unwrap we now have our value and we can go ahead and print those out to standard out just to see we'll do key here and value is here and key and value print line here you'll you'll notice a bit of a strange syntax of the exclamation point um that's that means that print line is a macro we won't be talking about macros here but just know print line happens to be a macro and anytime you use a macro it will end in an exclamation point that's just what that means and we can go ahead and run this i'm going to do cargo r which is just short for cargo run and you'll see that we have panicked and panic is another way to say that we've crashed the program well why have we crashed the program well we said it right here if there is no key will crash or if there is no value we'll crash so it's done exactly what we've said i expected it to do and in fact we see on what line it crashed on main three right here so this is the line where it's crashed and that uh we know exactly why it's crashed here so i'll have to actually pass some command line arguments to here there's a question macros like c plus or like in lisp it's more like in lisp um so hygienic macros if you're familiar with that term um it's not like cnc plus plus where it's just kind of string copy and paste macros but we won't that's probably all we'll talk about macros for today all right so let's pass in some key and i think we can just do foo and bar here as our key in our value here and if we run it again you'll see we've printed out to standard out key is foo value is bar so everything uh is working as we expected to so we have our key key and value here now what we have to do is actually save this persistently and so we're creating a key value database here how do we want to save this well the easiest way to do that is to write our database to to disk write it out to our file system and save it in a file somewhere so we're actually going to take our key in our value and save it in a file on on disk okay so how do we do that well a good thing for us to do let me pull this up again when we don't know how to do something in rust is to go over to docs.rs standard and it redirects so i'm going to show that to you again docs.rs slash standard right here and docs.rs standard redirects to this to the standard library documentation for us and the standard library documentation for us just like most documentation and rust is usually is quite good because the documentation tooling is built in with uh with rust you it comes with um restock which is a great uh documentation tool um and so of course if you're completely new to russ you might not know where to look i happen to know that everything related to file systems is in this standard fs module stan fs for for file system and this will give us a whole bunch of structs and functions and stuff like that that have to do with the file system and probably what we want to do here if we're looking through here um what do we want to do we don't really want to uh to create deer or anything like that we don't want to rub but hey this looks good what about right down here right right here says write a slice as the entire contents of a file we don't really know what a slice is but this sounds good this function will create a file if it doesn't exist this this sounds perfect and we even have an example down here where fs write we write to some file called foo.txt we write the um the text lorem ipsum and we can write you know several files here so this sounds this sounds really great we probably can can just use this and i'll go ahead and get rid of this real quick so we can say standard fs and then write and we'll have to provide a a file path and you know i think like um kv db or kv.db even better and then we're going to have to do something like contents here all right and of course you know if we want to go ahead and try and compile this we'll notice i don't it's uh rust compiler is telling us i don't know what contents is what what are you talking about there which makes sense we don't have this this contents variable so we should probably go ahead and create that and we now we have to think okay what do we actually want to store in our database well there's many different ways that we can do them do this some are more efficient than others but i think just a simple way to do it is to store it as a string where the key and the value are separated by a tab character and every key value pair is separated by a new line so that means inside of our database file you know it's going to be key one tab value one and then newline key2 tab value two and so on and so forth like that all right so our contents are going to be kind of a string with with these things in it and we need to format that string in this specific way there's another macro for that called format that allows us to format a string so it's just like it has the same exact syntax as print line except that instead of printing to standard out it just creates a string with uh that's that's formatted in that certain way so we said we want a key followed by a tab character followed by the value followed by the new line and we can say key and value like this and then we should check did that work oh hey looks like it did work all right so we're getting a warning a new warning up here that we're going to look at in just a second but just to make sure that we are you know doing things correctly here we can look inside of our kv dot db file here and you'll notice foo and presumably you know that's a tab character if we want to we can use uh xxd to look at this this is just a if you're not familiar with xd this is a command line utility for looking at binary files and you'll notice here what is it uh f o o and then zero nine zero nine i believe if i'm looking at oh this is great let me do it over here man ascii if we look at ascii yes all right so nine is a horizontal tab character so it looks like every everything is working out as we expect we've got foo followed by a tab followed by bar followed by a new line everything seems to be working all right so this is all well and good we did have an interesting error message not sorry not an error message a warning here saying unused standard result result that must be used so rust is trying to to help us out here russ is trying to tell us that we're using standard fs right but we're not actually hand handling the value that gets returned from fs right and if we go ahead and look here you'll notice that fsrite returns a result which is this thing so in rust you don't have a you don't have exceptions instead you have result types where you return back either a value that you want or an error type of some type and here standard io result is simply the the normal result type there is a just a normal result type you can see here which is just ok or error where the error type is an io error so we're returning back a result it's either a value or it's an io error and an io error is any error that can happen from um from an i o operation all right mr halsey welcome coming in and seeing what we're doing here with russ welcome everybody so there's a question does this mean rust has no void um so rust does not have void per se but you'll notice here inside of the return type for fs write it's returning this open parentheses close parentheses here and that is an empty tuple if you're familiar with a language construct called tuple also known as unit that's what we call it and if you're coming from a functional programming background this is going to be super familiar to you but if you're not you can basically think of tuple just like void all it is is simply a value that represents kind of a nothing like it's an uninteresting value oh and there was a question about type inference from above yes rust is is fully uh type inferred um and so you don't usually have to provide types there's sometimes where it can't quite figure it out but of course if it can't figure if it's if it's ambiguous what you mean rust will complain and say you gotta tell me exactly what type so here for instance we know that value here is an option string if we really wanted to be clear about it and let the whole world know that it's an option string we could write it like this so this is where we are telling uh rust this better be an option string or give me um give me an error here but you'll find that most of the time rust programmers just let the the compiler infer the the type here all right and there's uh there is a question about what are the main uses benefits of rust over other languages real quick rust is a is quite a low level language today we're not really seeing that we're talking about functional programming and option types and stuff like that and it may seem like we're talking about a high-level functional programming language that's sort of true but rust at the end of the day is a systems programming language so you can think of it as being in the same space as c and c plus plus that's usually what uh rust is used for the same things that c and c plus plus are used for operating systems browsers kind of low-level systems technology that other uh other software will run on top of um and we'll get into it a little bit later on in this stream but rust really cares about what is actually happening in the machine it only just provides you abstractions above that to where you don't have to worry about it unless there is an ambiguous way to make things more efficient and faster what that means is that rust offers something called zero cost abstractions that means when you create an abstraction and rust just like you would in any other programming language russ tries its real hardest to not um for that abstraction to be as performant as possible and if you're not using a abstraction you shouldn't have to pay for it real quick example of an extraction that you pay for all the time in other languages think about in a garbage collected language whether it's c-sharp or ruby or python or whatever no matter if you if your program simply adds two numbers together you pay the price of having a garbage collector because the garbage collector needs to start up there's a whole run time system that starts that is not a zero cost subtraction doesn't matter what you do you're always paying for that abstraction even if you don't use it and rust normally things don't work that way rust can run on bare metal you can completely remove any runtime dependency just like c by default rust on on linux will run with with lip c so there is a little bit of a runtime there just like c has but that's that's optional and you can completely remove it the reason that i'm interested in rust at microsoft and the reason that microsoft in general is really kind of starting to look at rust and take thrusters very serious is its safety properties unlike cnc plus plus rust is completely memory safe by default meaning you can't run into memory issues that you would run into with cmc plus rust does not have a garbage collector but just like languages with a garbage collector you don't have to worry about things like use after free double free all these memory safety issues that you run into with with other languages rust is completely memory safe and actually provides more guarantees than than garbage collected languages when it comes to these things so hopefully that was a quick explanation of why rust is interesting just just remember um although rough seems like a high level language we'll see um sometimes that's you know we don't get that advantage and we actually have to really care about um uh what's happening uh on the machine all right um there's a question about uh haskell and and rust traits um you can think of of rush traits a lot like haskell type classes but we won't get into that and by the way if you're not into functional programming at all don't worry russ might be a doorway for you but most of the time you won't even know that you're you're doing a lot of functional programming at the end of the day rust isn't a functional programming language it's an imperative programming language but it takes a lot of influences from functional programming languages all right i hope i got everybody's question there um uh there's a question about constructor destructors um yes there are construct there are not constructors uh and and rust but there are destructors which we won't talk about today unfortunately um this is being recorded and i think it will be available on youtube uh later on um so if you haven't caught everything you can watch this again all right this is awesome keep the questions coming that's really great so far we have a working database here but of course there's a huge bug in it which hopefully is very easy to spot and that is every time we run this no matter what we provide um when we come here and run by the way real quick i'm just gonna run cargo build here that remember that builds um the uh the program but does not run it if you want to run the binary directly you can go into target debug and then the name of the program which is in our case kb store and then run it like this so it's just a plain executable and of course we're crashing here because we're not providing a key and a value so hello world here let me go ahead and we can run you know hello world here and if we look at our kv.db here hello world is there but of course if we write hello velt which is the german word for world i am i live in germany you can see when we print it out we get hello about so we're basically overriding our keys and our values every time that's not you know the most useful database in the world where we can only keep one key in value at a time so really we need to go ahead and um be able to read in our database add our keys and our values and then store that back out so we can we can keep on going here um you know inside of our main function but i think this is a really great time to start abstracting out the the idea of our database and the way that we're going to do that is by creating a struct called database now rest is not an object-oriented language it doesn't have classes um it has structs very very similar to to see um or to go uh and so here we have a struct called database and structs are just kind of um named collections of values right and so our database here is going to to hold on to keys and values now what kind of data structure is really great at holding on to keys and values um well when i think about that i think about hash maps and in fact russ comes with a hashmap type in its standard library so we're going to store a hash map inside of our database struct and hash maps are available in standard collections hash map now sorry can't type now if we if we try to do this and actually run it we'll notice we get a compiler error here saying hey i expected at least two type arguments in hashmap if we look up here wrong number of type arguments expected at least two found zero and that's because rust has generics we can't it's not good enough to say we want a hashmap we have to say a hashmap of what kind of keys and what kind of values we have to provide actual concrete types for the key and the value type that we're storing and so for us i think it's best if we just store a string as the key and string as the value so this is what generics and rust look like our hash map is now is now whole and the type of key that we have is string and the type of value that we have is also string and of course we didn't have to pick these but for our particular program this makes a lot of sense all right so structs are all well and good but it makes a lot of sense if we go ahead and add functionality to our struct um and this is where while i said rust is not an object-oriented language it does provide some of some of the fun conveniences of object-oriented oriented languages without some of the problems so we can add functionality to our our struct here by saying i want to provide an implementation for this for my type database and i'm going to add some functions and methods to it now the first thing that we're going to do is add a constructor now i know i said there are no constructors in rust but you'll see in just a second what i mean by that we're creating a function here called new and it's going to return a database and this is what functions look like uh and rust very similar to the main function here except that's inside of our impul here meaning that it will be kind of attached to this database type and we can return a database here by saying standard collections hash map new and you can see here hashmap has a new function as well and this creates the hashmap just like this new function here creates the database now i said before like there are no constructors and russ which is true like this is just a function there's nothing special about it it constructs a database and i called it new but that's completely arbitrary i could have had the same function called foo or create or whatever i wanted to call it new is just a convention for functions that create types usually you use the function name new but you don't have to so there aren't constructors in in rust and in the sense of constructors being a special language feature they there just happens to be a convention for functions that create types being called new that's all all right hopefully this makes sense to everybody if it doesn't um let me know um so enter is not a keyword right here uh inner is also in in rus we have structs uh that have values inside of them so we can have you know another thing like uh you know foo is going to be a string here and bar is going to be um i'm trying to not introduce too many new types here u8 an unsigned 8-bit integer this is simply inner is i happen to call it inner if you can think of a better name for it like you know we could call it hashmap here that's also fine it's not a keyword it's just saying there's a field inside of database that we have called enter that's all there's a question is this struck like an interface and you provide its implementation no so structs are concrete types database i can tell you a database in memory will be however big you know roughly that a hash map uh is so probably three probably three words big so what is that uh 24 bytes long i mean this is getting way ahead of ourselves here but this is a real concrete type struct impul here just means we're adding functionality to it we could have free functions like this and the difference is right now when we want to call this new function let's say we want to call it up here we would do this database colon colon new we've now called that new function if we remove the impul here this will no longer work and we just have to call new as a as a free function like that but this is this is much nicer the stream will be published later on youtube so look out for it there um the the uh the link was posted above it's youtube.com microsoft reactor all right so hopefully this makes sense to everybody here we are creating a database we're calling the new function that's associated with that database type we could have called it anything we wanted to but by convention and rust when we're creating a type when we're logically creating the type we usually call that function new but we can call it whatever we want and this creates our database here this is really great so we have a database here it has it has empty things inside of it um an empty hash map new on on hashmap it's just an empty hashmap it has no keys and values inside of it one thing that i like to do here is we keep repeating the standard colon colon collections colon colon hash map that's there there's so many you know it's really long i would really like to just say hashmap instead the way you can do that is by you saying use standard collections hashmap and this brings that hashmap name into scope so that when we call when we just refer to hashmap here it knows we're referring to standard collections hashmap so this is the same thing we've now brought standard collections hash map into scope yes so we renamed inner to hashmap here so um i'm gonna keep it as enter because that's what i like to do is call it enter the reason for it is because it's our inner type i know it's we can we can bike shed names here um i'll call it inner for now and we can we can decide what we want to call it later on all right so we have a database it's completely empty but this isn't really what we want right we want to create our database and when we're creating it we want to first read the database file get all the values from the database file and store that in our hashmap so that our hashmap contains the inner hash map contains the keys and values that were in our database file so what does that mean we have to read the database file and we have to store the value we have to parse the values inside of that file and then store them into the hash map so let's go ahead and do that and we can go back to our friend our friendly documentation over here standard fs again and figure out okay how do you read an actual file there's a bunch of ways to read we can you know read into a re-bytes we can read a whole directory we can read a link but read to string is probably what we want here read to string simply is reads the entire contents of a file as a string and you'll notice here that it returns a result of a string so of course reading from a file can fail the the file might not be there or there might be some disk error or something like that so this is important to know read to string can fail here all right so let's go ahead and do that real quick we can say contents equals standard fs read to string and now we pass in our um our path here and what was it it was kv dot db so the question is what should we do here when read to string fails what do we want to do now before what we were doing is we were calling this unwrap function which uh we called unwrap on options before results are the same thing if it's if the value is an error it will crash the program so this is fine we can totally do this we can call unwrap and if read to string fails we'll just completely kill the entire program that really helps for kind of quick programs like we're working on right now but we we maybe want to handle this error a bit more elegantly and the way that we can do that is by saying that instead of our new function here returning a database this means this this function will always return a database can't error it might crash the program might crash but there won't be there's no way to handle that crash if we want errors that we can actually handle we need to return a result type ourselves so here we're going to return result of either a database or some error type now error here is something i made up error doesn't exist and in fact if we try and run this now we'll get an error here saying i don't know what error is can't find the type error tell me what error is and so we need to provide an actual concrete error type now one that we can do is standard io error which we've seen before standard io error is right here and these are any errors that have to do with i o operations so we're going to do that for now we're going to return a result where if everything is fine we get a database back or we return an io error an error inside of doing some some i o all right so how do we actually return this error well there's multiple ways that we can do this one way that we can do it is using pattern matching so that's what the first thing that we're going to do we're going to use pattern matching which again if you're coming from uh if you are i'm going to make this slightly smaller hopefully this is still okay let me know chat if it's too small if you're coming from functional programming pattern matching will will be very familiar to you if you're not you can think of pattern matching as being just like a switch statement from c or javascript or whatever but just much better and we'll see how so we're going to match on what is returned from read to string and if read to string returns back okay meaning everything was good then inside of that okay is going to be our contents and then once we have those contents we can do something with them so we're going to just return them back from this match now this might be surprising to you because you're coming from a language that where you have the difference between expressions and statements and rust basically almost everything is an expression so we're just returning contents here and assigning it to this outer contents variable here i'll make that clearer by calling this c and this c so we're unpacking c from ok and then returning it into this contents variable here now if we go ahead and try and compile this oops if we try and compile this here we'll get another error we will not get the error that i want it to get this is a different error let me fix that real quick we have to return the result from this function so here you can see once we reach this line we're going to return okay like okay meaning everything's good and if you want to know what okay actually is like where is this okay coming from we look at this is the result type that we're we're looking at we haven't talked about enums quite yet but what enums are are um uh basically like one it's either one variant of the enum in this case okay or it's the other variant of the num error so when we talk about a result we're talking about one of two things it's either okay meaning everything's good or it's an error and we can match against that result and see okay is it is it okay or is it an error and do different things depending on which it is in other words okay is everything is good so result is a type that represents either a success or a failure and now if we go ahead and compile this you will see that in pattern matching everything needs to be exhausted so we've handled the case where everything was okay but we didn't handle the case where there was an error and you'll see that the compiler is telling us like hey you didn't cover the error case here you gotta cover that if you don't cover it i will error at you and we won't even be able to run the program so what we can do is handle the error case we get some error like this and just like with ok we're binding the actual error type to this name e here we can or we can call it error whatever we want to call it and then we can do something with that one thing we might want to do is return back that error out from the function so we're actually returning the error from the function just like this so again what is this doing we're calling standard fs read to string which returns back to us a result and we know a result can either be okay everything was good or error and what match allows us to do is say which one is it if it's okay take the value that was inside of okay and do something with it now we could do more than just you know put it back into contents we can do anything with it here we can we can have a whole uh a whole bunch of code in there but we're just returning this into contents or if it's an error then the thing inside of that er variant call it error up and this needs to be error and just return it back out from the function so by the time that we get here at the end of this line contents will just contain what it was ever in c there's a question of are is okay and error methods or types they're not really either okay and error are variants of the result enum so another way we could write this is like this so it's either result okay variant or result error variant and this is really kind of if you're coming from a um a non-functional background this might be the most kind of mind-blowing thing here but this is this is kind of key to to rust programming now if you're a very if you're a low-level programmer and wondering what the heck um how does this actually you know map into memory these are just uh effectively just like tagged unions so in memory there is uh probably usually a byte let's say that represents uh is it okay is it an error and then there is a data section attached to that that contains um the data associated with it the thing that makes this much better than tagged unions and c is that we if we get okay we can't read out an error or if we get an error we can't read out uh the the thing inside of the contents inside of okay like it we can't screw this up russ will not allow us to screw this up and earl tease me i'm not uh i'm i'm not going to get into that that's that's a bit too too meta of a point right now but yes you are correct um okay so this is a very common pattern this this con there's a very very common pattern where we get back a result and if it's the okay variant we just want the thing inside of ok and if it's an error variant we want to return back from the function with that error this is so common that after a while in russ they came up with a specific syntax just for this pattern to unwrap the value if it's if it's okay and return the error if it's not and it's this right here the question mark at the end so all that code is equivalent to this right here and if you're a rust programmer this becomes very very very easy to see when you're reading the code you just know okay read the string fails and if there's an error it will return the error back out it's very very short very elegant and of course impossible to mess up because if you do mess it up somehow the um the compiler will complain at you all right so we have our contents here as a string because if it was okay we got okay we unpacked the the string inside of okay and packed it in here into the contents binding here so this is all well and good um somebody in chat is saying the compiler is your friend that is absolutely true a lot of people talk about fighting with the compiler when they're starting out with learning rust the sooner you can rephrase or look at it differently in your mind to know that the compiler is your friend to where the compiler actually wants you to write correct programs the better you're going to to handle the struggles that you will have at the beginning with rust the the great thing about rust is when it compiles a lot of times the program works much much much more often than in other languages i almost never use a debugger in rust because um if i have an error it's usually quite easy to find with some print line debugging real quick just thrown in there it's usually some logic error i have used a debugger and rust before for unsafe code which we won't talk about today that's if you really need to do strange and funny things uh in rust and then all the all the wonderful safety checks um that you have in uh in safe rust are no longer there are they are still there but there are things you can do that you can't do in safe rust and sometimes that that does mean you have to use a a debugger but in normal rust i almost never use a debugger because of this property um there's a real quick uh question about russ versus go my cop-out answer to that real quick is learn both because learning more languages uh is awesome and go is a great a great tool for many things so learn learn go and rust the the more specific answer to that is if you are looking to do uh cloud-based tooling around docker or kubernetes or something like that then oh wow go is a great tool for that although russ is gaining some steam there so so if that's really your your jam then go for go um if you're more interested in low-level programming where you want to get close to the machine maybe doing some iot and embedded programming or learn how operating systems work or something like that then rust is a great tool for that so they both have pros and cons they're both used for for different things learn both there's a question about when i say we won't talk about this today um is there is there a series for rust um so there will be multiple uh streams i'm gonna be on tomorrow um around the same time uh talking about the borrow checker and rust and we're gonna learn we're gonna peek a little bit more into the borrow checker because that's typically the thing that people struggle with the most when learning rust later on in the week there's other series around rust they will be a little bit more a little bit more talking a little bit less coding a little bit more about pros and cons of rust why we're interested at rust at microsoft so if you want to get a good feel for what rust is good for and stuff like that and and why rust is such an interesting language tune into those um i have my own stream uh streaming channel that we'll talk about later uh twitch.tv ryan levick r-y-a-n-l-e-v-i-c-k um you can you can catch me there um and i i stream about rust all the time so if this is interesting to you and there's uh and you would like to um to see more of this i'd be happy to continue this series on my own channel after we have returned the the microsoft developer channel back to other other tools and other people who want to talk about other things as far as the question mark here this is this is pure syntax so it's not really a macro it's just a part of the language it's been built in to the language it's just another piece of syntax um there it's a little bit more uh flexible than i hinted at that it don't doesn't only work with result types but for for all intensive purposes right now you can just think of it as handling the pattern that we looked at specifically all right so we're we're reaching um 30 minutes left in our stream here so i want to make sure that we go ahead and and continue down the road here and try and get our database parsed here so we have our content string here this contains the entire contents of the string of the file as a string here and we need to be able to to parse that string and so i think the best thing to do um is to call contents dot lines i believe that's what it's called and what contents dot lines does is give you another iterator over the over a string um this time with every line so every chunk of the the string separated by um uh newline characters um and it also handles um carriage returns on windows and stuff like that and so in order to iterate over the lines we could use a for loop uh and rust so you can do something like for line and for line and contents dot lines and now inside of our for lip we have access to to the each line in turn and so let's go ahead and just run print line here and we'll go ahead and just print out the line now um this is very helpful and stuff like that one thing that we could do instead of this is use the dbg debug macro here and this provides a little bit more information when we're debugging this is great for debugging hence the name and so you can use this instead and we'll see what the output looks like for that so you'll notice this is the debug output here we have line is equal to hello and then the uh the tab character and then vet here all right so this is all looking good this is exactly what we would expect so now we want to break up our line into two things separated by um by a tab character so how do we do that well we can use dot split and dot split takes in a i believe a character like this um and splits it based on on that character and this again gives you another iterator so we'll call this chunks so this is giving us everything split by um by a tab character and of course if our line has more tab characters in it we'll have more chunks and stuff like that so we want a way now there's many ways to handle this i'm going to go down a route that i actually don't think i would do if i were really writing this code but i think it's good to to explore it we're going to take our chunks separated by tab characters and we're going to store it into a vector and vectors are just linear collections of things like arrays except that they can grow at uh at run time so rust has arrays but rays are always fixed size and vectors on the other hand can grow so if we want to add elements to them we can do that so how do we actually collect these items into a vector how do we put the items uh inside of our iterator into a vector well there's a very convenient function for that called collect and what collect does is it takes all it runs through the iterator getting all of the elements and then puts it into a collection of your choosing and this sort of seems like magic it's really not but how this actually works is i have a stream on that this is not really um the point of this stream so we won't really get into it but we're going to collect our strings into a vector like this again when we call split we're getting an iterator of each element in the in the line separated by a tab character and collect then collects all of those elements and puts it into a vector for us okay now this will not compile and the reason for that is because it's saying i don't know how to build a vector of strings from an iterator of ampersand stir what the heck is this so i said that split actually is an iterator over iterator over our line that yields us strings but that's not really true and this is where we first start seeing that rust is a low level programming language because rust has many types of string types and that's because strings are very complicated and most languages just say well we'll hide all that complexity in our standard library and try and expose to a to the end user a nice simple api which is great for those languages that's that's really the perfect choice for them for rust as a systems programming language a low level programming language that is not something that i can afford to do the complexity of strings must be exposed to the end user so the end user has control over that complexity and can make decisions that work best for them that's because we need to be able to build any kind of software like operating systems where you have every last control over what the machine is actually doing so um what is this ampersand stir type um and in fact let me go ahead and write it this is actually the correct return type here ampersand stir what that is is the capital s string type is what's known as an owned string this gets into the ownership system of rust which we're going to talk a lot about in tomorrow when we talk about the the borrow checker and things like that but own strings are when what that means is when the string is no longer in scope it gets destroyed it gets effectively garbage collected but this garbage collection doesn't happen at runtime it's all at compile time that these that this is decided so at the end of the scope when a string capital s string goes out of scope it will uh be destroyed and for strings what does destruction mean it means that we'll call free on the the string data that lives in the heap so again rust low level language you have to care about the stack versus the heap and things like that strings live on the heap capital s strings live on the heap and when they go out of scope their destructor gets called and the destructor frees the memory that lives on the heap there's a question of can you free manually you can call drop the drop function which yeah calls the destructor early now we're gonna tomorrow come come back tomorrow we'll talk about how if you try to to call drop on a value twice in a row it will not compile so there is no use after free or anything like that we'll talk about the borrow checker tomorrow that makes sure that we don't um mess up the this this manual memory management and that's really the promise of rus rus is a manually memory managed language just like cnc plus plus except the compiler has all these smarts built in to it to where if you are using rust you cannot make a mistake and mess up your memory management it is possible to leak some memory you might it is technically possible although quite difficult to not free up your memory when you should but you will never ever ever in safe rust be able to call free twice on a particular value or use a value after it's been called after free has been called and in fact uh dark watering chat is saying you pretty much never have to to call free manually that's true you basically almost never ever have to manually call free because rust has the raii pattern built into it if you're from c plus you know what this is where um when values are no longer in scope their destructors are run um and c plus has this kind of attached to it in the language this is fundamental and baked into to rust as a as a programming language i'm going to turn on my light real quick i'm over here in europe so it's a little bit late uh the sun is going down so hello to everybody else in europe and to everybody who's not in europe i hope you're enjoying your day all right so capital s strings they're heap allocated when they go out of scope they'll be destroyed their memory will be freed what is this ampersand stir here this is the first time we're looking at a borrowed value this is a value that when it goes out of scope it will not be destroyed so ampersand stir here is simply what's called a string slice and what a string slice is it's a view into string data that lives somewhere else and when we no longer use that that string view anymore it doesn't mean that the string data will be freed it just means we don't use it anymore so ampersand string here is simply a view into other string data now why do we have that let's think about this and in fact what type is line and a great tool for figuring out what type something is when you're uh when your code completion isn't working like mine is right now um we can just say foo is going to be equal to line foo is equal to line here and we're saying hey the type has to be unit which it's not unit but the compiler then will tell us what type it is saying hey i expected this to be unit but it's actually an ampersand stir so line here is itself an ampersand sir that means line here is ampersand stir it means all of these uh these chunks inside of our vector here are ampersand stirs why is that contents itself is a string capital s string that means when contents goes out of scope its memory will be freed but when we look at each line and each chunk in the line we're actually looking at the string data that is on the heap and owned by this string variable here if this weren't the case what we would have to do is every time we get we get another line we'd have to allocate memory on the heap and uh copy the the memory from our content string over to our line string but with ampersand stir it's simply a pointer saying hey the the line data exists here it starts here it's this long um and for for line.split it's saying hey all of the chunks here um this is where they live on the heap and this is how long they are and that's all that it is so we don't have to allocate extra memory and this is very important for rust it means that we know exactly when we're allocating memory it doesn't uh and means that we we can have control of over when memory gets allocated unlike in a garbage collected language which for a very good reason garbage collected language will simply would would simply copy that data for us because it's much more it's much simpler but we're in a systems programming language here we we can't afford um that level of simplicity because we have to have full control here um all right so i hope this uh answer is is starting to become a little bit clear about what's going on here so there's a question of how come our hashmap isn't ampersandster and perc uh hashmap ampersand stir ampersand stir instead of hashmap capital s string capital string we could change that but that makes our our our program much more complicated and we'll talk a lot about this tomorrow in our overview of the um of the borrow checker um we're we're instead saying here that the string data is owned by our database struct here and that means that as long as our database struct is still around the string data is still going to be around as well we're tying these two values together and on these strings will only be freed their their memory in the heap will only be freed when database itself is freed uh is is destroyed and again tomorrow is really the day that we hone in on this so just just giving you like a little bit of a sense of of what this is like in inside of us if you you if you've not done rust before you more than likely are only have the vagus idea of what i'm talking about right now that's fine that's totally cool come tomorrow we'll learn more about it we'll get an even better feel for it tomorrow this is the promise of rust and also the thing that people struggle with the most so come come back tomorrow all right we got about 10 minutes left so i really want to to get through this here we've got our our our chunks here now let's say our line in our database file had like 10 tabs in it um it would obviously be a malformed uh database file but we need to handle that case right so we need to say like how many chunks are there if they're if the chunks length if the number of chunks that there are is not equal to 2 then we need to like return an error and that for now i'm going to use this to do macro which is very helpful um that allows me to to specify to do's that i have in my code um if we run the code and reach this point the the program will crash um but it's a very simple way to instead of using comments and stuff like that like you would in in many other languages like this you can just use this to do macro and it's machine readable machine searchable and say you know return error so that's on our to-do list now and there are tools that parse this and provide to-do's uh that your code base has so if our if we have if we don't have exactly two chunks then return an error otherwise our key is going to be the first chunk and our value is going to be the second chunk just like this now we want to take the key and the value when we want to store them into our hash map so that requires us to bring this hash map up here initialize it at the top so we'll say enter now we're going to be mutating this hash map as we go along right we're going to be adding values to it we're going to be mutating it and remember and rust we have to be explicit about that so now we have our hash map here and we want to insert the key and the value into our hash map so we call enter.insert with the key and the value and this will not compile and the reason for that um is because and in fact this is very interesting for how rust is inferring types we it says here that hey this inner value i expected it to be a hash map of string capital s strings but you're providing me a hash map of ampersand stirs here and indeed that's exactly what we're doing we key and value here are ampersand stirs they're views into our contents string here but as we said before we'd like for our hashmap to actually have capital s strings for reasons that we'll talk about tomorrow um capital s strings are very good um for for our use case we don't want to um uh to store views into this string here because and this is the reason at the end of our new function here contents goes out of scope and it will be freed so we can't have views into our contents thing that outlives this function because at the end of this function the content string will be freed and we would have dangling pointers and rust doesn't allow us to have dangling pointers we don't have to worry about making that mistake because even if we tried to make it which we'll we'll try that tomorrow russ will complain at us and say hey you can't do that so instead we'd like to convert our our string views our ampersand stirs into capital s strings owned strings how do we do that well i think my favorite way to do that is to say two owned this takes ampersand stirs and takes really any borrowed value any value where we don't own the value and it turns it into the owned version of it and again where we only have a vague understanding of what ownership is but capital s strings are owned strings and ampersand stirs are borrowed strings and so we're going from a borrowed string to an own string by calling to owned here and going from a borrowed string to a an own string necessitates that we allocate memory so we can tell from this call here it's very plain any any rus programmer will be able to come in and say we are allocating memory right here we know exactly where we're doing it we know exactly where our memory will be freed this content string will be freed right here we're allocating memory right here and we know when those strings will be freed that's when our database gets freed at this line so this is exactly how russ gets away with its um with the the the fact that we don't have to call free manually it is very easy to statically know when when values will be freed and so we just pointed out okay the value will be freed here this value will be free down here the compiler obviously knows that as well and it inserts calls automatically for you to call free or to call the destructor for that value on your behalf all right and if i'm not mistaken and i forgot to save here this should compile oh and we still have our weird foo thing up here just to see what our type was this needs to to go away this should compile there we go so we have a way now of uh reading our database kb.db file and storing it in our database file now we're running out of time and i want to leave some time for questions so we're not going to get any further than this we still up at the top here are simply ignoring our database and writing the contents to our kb.db file so we still have the bug that we had before but as an exercise to the reader read a little bit about methods and rust and how we can create a simply an insert method that inserts these key and values up here into the database and then maybe you have something like a a flush or a write method that takes the contents of the hash map and converts them into the proper format and writes it back out to disk and and if you do that then you have a simple key and value database thing here unfortunately we don't have a ton more time so i'm going to leave the exercise there and answer any remaining questions that you have for the the next five minutes but i hope um that even though you may not understand every single line that you have here you have a good idea of what rust is all about so first question here method is different than a function a method is simply a function that takes um the the receiver the the struct that you're calling the method on as its first argument and so real quick what that syntax looks like is you know function my method here its first argument is going to be self or self like this and self here refers to the database and we can call my method up up here like this db my method so methods at the end of the day are basically um basically just like functions except that they have a little bit of a different syntax and their first argument is always uh self is always the struct that you're calling the method on um real quick i'm going to remind you of uh of my weekly stream here so let's go ahead and here's the information here you can find me on twitter at ryan underscore levick i tweet a lot about rust and you can find me on twitch twitch.tv ryan levick that is where i stream about rust usually every week or every other week and i have not done a beginner series so if you're interested in learning more and continuing this series on my stream over there then definitely check me out give me a follow and let me know what you thought here feedback is always welcome and of course come back tomorrow same time at 9 00 a.m pacific time um 6 p.m uh european time and we will continue to talk about russ but through the perspective of the borrow checker and how to manage memory uh so check that out all right we have a few more minutes um for for some questions just to finish it up um so is russ used a lot inside of microsoft um i would not say that rust is used a lot inside of microsoft but the usage of rust is definitely growing it's a very interesting language we are very interested at microsoft and rus for its safety properties because we have found that even very very experienced cnc plus plus developers continually make mistakes around memory management and those mistakes cause a lot of security vulnerabilities and if you use safe rust those mistakes are impossible to make the the compiler will just simply not allow you to uh compile a program with those types of mistakes in it and so there is um an effort inside of microsoft to con convert especially security uh critical software over to rust away from cnc plus plus um and so we have an effort on going to to do that so i imagine rust usage in microsoft will grow considerably in the next few years but stay tuned we'll we'll be blogging more about that um in the future um all right so what about child parent-child relationship between string and ampersand stir that is so the question is if you have a capital s string and an ampersand stir and the ampersand star is pointing into the capital s string it's taking a view into the capital s string how does it know when the capital s string will be destroyed and the ampersand stir is no longer usable that is the borrow checker you basically that's exactly what the borrow checker does and we'll be talking about that tomorrow exactly how that works um the question of what do i specifically do with rust inside of microsoft i specifically am a developer advocate in charge of rust so i engage with community i do streams like this i help internal teams adopt rust learn more about the rust be a liaison between the rest community and microsoft and so anything and everything microsoft and russ related i'm i usually have uh my hands in it whether people like it or not and um that's uh that's my job so um so i wanna i wanna leave uh everybody with a few words about continuing your journey of course i said you can follow me we'll be back here tomorrow for um oh and in fact we're doing the stream today at 4 pm pacific okay so that's today the next stream um 4 p.m per specific pacific sorry on like an interview about uh the rust language microsoft and rust so definitely tune into that that should be it should be really great and really when you're continuing your journey with russ know that while it can be difficult at the beginning to learn the language there is a steep learning curve at the end of the day the language is quite quite self-contained and once you get over that initial hump it's it's quite easy to be very very productive and rust and to not have to worry about a whole slew of problems that you have to worry about in other languages not only c and c plus plus but even in high level languages like java and c plus plus and swift and whatever there are constructs and rusts that prevent you from making common programming errors that are are very common even in garbage collecting languages and so when you're learning it know that it is big it is tough to begin with but once you get through that um it is real pleasure of a language to use it's a lot of fun i enjoy it quite a bit and if you ever have any questions feel free to reach out and with that being said i want to thank you all it's been a pleasure um i hope to see you all uh tomorrow i won't be here tonight uh for for the stream um at 4 p.m pacific but i hope you are i'll be in bed but i hope you enjoy it um and we'll be back tomorrow and i can answer any remaining questions you might have so everybody enjoy your day or your evening or your night and hope to see you around in a future stream somewhere out there in the ether have a good day
Info
Channel: Microsoft Reactor
Views: 15,060
Rating: undefined out of 5
Keywords: coding, languages, framework, rust, frameworks, Rust
Id: 5dRT_v3hrZ0
Channel Id: undefined
Length: 87min 15sec (5235 seconds)
Published: Fri Sep 25 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.