Introduction to Rust Part 1

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
all right welcome everybody to another stream this is a very exciting one because today we are going to be focusing on absolute beginners uh to the rust programming language today is going to be introduction to rust um the only kind of background knowledge that that will be assumed is some programming knowledge of some sort from any language um out there uh javascript c sharp java swift uh c c plus plus you name it um as long as you have some idea of what programming is then hopefully you should be able to follow along today um and because of that we're going to take it slow we're going to be answering a bunch of questions some of those questions might be obvious to you to some of you coming from certain backgrounds and might feel advanced to others coming from other backgrounds um and so we'll make sure to to uh address what kind of audience uh each question is for um but really today is all about learning rust from scratch um and we have the help of chat today to ask any and all questions um so that you know even if you're watching this later on on youtube um hopefully uh your questions will be answered and i'm really going to try and take take it slow and um even go down some paths that are not exactly you know the right way to do things so that we can see some error messages see what happens when things go wrong um so that when you're out there trying this stuff on your own um you know how to approach uh and and overcome the errors that you might see all right without further ado i'm gonna uh open it up just talking a little bit about how to learn rust so once you're beyond this this video today that you can kind of continue your learning so i'm going to switch over here to the screen oops sorry about that and uh talk a little bit about um the important uh kind of beginner um uh material that you might have in order to uh learn how to program and rust and i think the first one really is the rust programming language book so if you've not checked out this book before you should be able to see up here doc.wrestling.org table book the rus programming language by steve klapnik and carol nichols is a really fantastic introduction to the language and unlike other languages rust really is it's important to kind of get a fundamental understanding of the concepts in rust before you start just hacking around and i know for a lot of people out there they love to just get their hands dirty get in there and try something out there's plenty of time to hack and and rust but i think uh i would it would be it's it's very important to take a step back take a breath read at least through the first i would say four chapters of this book to get an understanding and we'll go through all that material today um before you start really kind of diving in because if you don't get that base understanding you might end up getting frustrated um and that that would be a shame um all right what do we have else here we have rust by example is another great one if you want to see kind of the same material but covered through code examples rust by example is a really fantastic resource to check out and then one that i've not personally tried myself because this came out after i had already learned rust but i've heard great things about it is rustlings which is kind of an interactive tutorial for learning rust in your terminal so i think this is a really great one um to to go through as well um great so if you're following along with us the first thing that you're going to want to go ahead and do is go to rustup.rs and install uh rust this installs the rust up tool which is a kind of tool manager for managing all the various tools around rust that's the compiler various tool chains so if you're cross compiling i'm on windows for instance right here i'll be on windows subsystem for linux today but let's say you're on mac and you want to build a binary for linux you can also use rust up to ensure that you have a compiler that is capable of doing that comes with core tooling around rust that we'll talk about today including the cargo build tool so you want to go ahead and come here and install rust for your machine and and once you have that we'll go through how to make sure that that is working properly so go to rustup.rs and get that installed and then if you're like me and you use visual studio code then the rust analyzer plugin is a really fantastic plugin for vs code that gives you all the kind of really great ide like experience uh for rust um so i would definitely recommend that for vs code rust analyzer does i believe also work um for for other uh editors um you know like them and emacs and stuff like that but i think the kind of premier experiences through vs code so if you're not sure what to use um and are okay with giving vs code a try even if it's not your go-to editor then rust analyzer is definitely uh the one that i would recommend for you inside of vs code so go ahead and get that all set up if you're watching in the future you can go ahead and pause now while you while you get that done if you're watching live right now hopefully if you have any questions about that if you run into any issues let me know and we can go through that but this is what we need to get set up and so once you run uh through that oh and where is my terminal here once you run through that here i am on uh the windows subsystem for linux you should have installed on your machine then rest up which you can see here and this is a way to install the rust compiler and various things but when you install rust up it already installs the compiler for your particular machine so you should be good to go and also it installs the cargo build tool which we can check out here by typing in cargo and what cargo is for those unfamiliar cargo is rust's build tool package manager all kind of rolled up and run um it also allows you to bootstrap uh new projects which we'll see uh in just a second here so cargo is kind of the interface for um for creating and interacting with a rust-based project one thing that you also have on your machine now is rust c that's the rust compiler most of the time you won't actually be interacting directly with russ c you'll be interacting with cargo and cargo then invokes rest c but this is the actual rust compiler that compiles your code and we'll see what that looks like in in just a second all right so we're going to create a new project here um and a new cargo project here so that we can go ahead and get started but before we do let's talk a little bit about what we're actually going to be doing today to to learn rust what we're going to be doing today is creating a simple command line utility for a key value store database now i've done this project before as part of the microsoft reactors series so if you caught that then this will be a little bit of a review for you but in that video we only got um to the beginning of it we only got uh so far we're gonna be completing that project through this stream series um and so the first part of this will look familiar if you've caught that before but the rest uh is entirely new and if you haven't seen that series before then then all of this will be new for you so we're going to be building a command line utility database where we can uh do something like type in like well i think we'll call it kv store so we'll have a command line program here kb store and then we can do something like um you know eventually it will be like heavy source stat hello world and then you run that and it will set in your database uh the key hello with the value world and then uh you will be able to do something like you know git hello and then it will return back world to you so that's the ultimate goal that we want to get through that's it should be there's plenty in there for us to run through and get get familiar with uh with rust in that and so how do we actually get started with this what is the the first step that we we will do well it's important um that to know that the first step in any rust project uh is to start a new cargo project so we're going to invoke cargo here cargo new and then we're going to call the project kv store like this and you can see that cargo has now said that it's created a binary application package called kb store and we can also create libraries with cargo and various types of libraries and stuff like that but by default cargo is going to create us a binary application package and that's exactly what we want because we're creating an actual application here called kbstore and then we can go into kb store here and take a look around and i'll just run tree here to take a look at what is actually inside of our kb store directory here and it's very simple kb store basically comes with two files um one of those files in a directory um the source directory is where our code is going to live and right now we have a main.rs file that's where our russ code lives and we'll see main.rs quickly in just a second and it's very simple um and then we also have a cargo.tamo file and this is a manifest file that tells um cargo about our project so we can take a look at cargo.tamal here and it's very simple um it just is some metadata about our about our package about our um our actual app here um the package is called kb store we the version is 0.1.0 i'm the author and then addition is something that we won't talk about today but that's also set to 20 the edition 2018. and you can see there's also space in here for dependencies and there's lots of other things that we can put in our tamil file that we'll we'll look at eventually all right and then we can take a look at our main dot rs file this is our rust code here and it's very simple here our rust code is quite straightforward it's a main function that calls print line and so like a lot of other languages the way that rust works is if you have a binary application the main function is the function that gets run first and this is what russ code looks like we have fn keyword and this is this is a function in rust we'll be seeing a lot more of those and then we have this print line macro and we know it's a macro we don't know what macros are right now but we know it's a macro because it has this exclamation point at the end so we'll we'll talk later on in the tutorial about the difference between functions and macros but this is a print line macro that prints the string hello world to standard out all right so how do we actually run uh this this package cargo has kind of bootstrapped it with a hello world uh type thing how can we go ahead and actually see this in action well the first thing to do and the thing that you'll probably end up doing quite often in rust projects is first checking to make sure that your code actually type checks and is correct um hopefully our code is correct because it's we haven't actually written any it's just the the kind of sample code that cargo puts in there for us but anyway we can run cargo check and that will type check our our rust program and it says here it's checking that everything is good and it finished that and 0.22 uh seconds so everything is good that's that's good news there we can also um build our project by doing cargo build and this checks the the uh program just like cargo check does and actually creates the binary so cargo check didn't create a binary but cargo build will create the in binary for us so we can run cargo build and you'll notice now that we now have an additional two things in our project we have a cargo.lock file and a target file target is where all of our build artifacts are including our end binary and cargo.lock is a is a way of of explaining to cargo like these are the actual locked versions of our dependencies that we have now we don't have any dependencies so if we look at cargo.lock this this will not be very interesting cargo.lock doesn't have any um any information about our dependencies because we don't have any dependencies so it's very very um simple file and and it says even up top this is generated manually don't touch it it's it's not uh intended for you to touch all right um but what about this target file well if we look at tree you can see here it's got a whole bunch of stuff in here but the important thing is if we look inside of let's clear the screen here if we look inside of target debug and here you'll notice among other things is this kv store file here and if we go ahead and run target debug kb store then it actually runs it so that's our end binary we've actually built it and you'll notice it's in the debug folder because we have built a debug version what is the debug version a debug version is it comes with more debug symbols inside of it don't worry if you don't know what symbols are it comes with more debug information inside of it and typically it's slower than a release version would be because the compiler doesn't do as many optimizations to it why is that because when you build a release version which we'll see how to do that in just a second the compiler tries its hardest to make the fastest binary that it possibly can by default and that takes time um and you know for our simple hello world project here there's not really a huge difference between a debug version and a release version in terms of how long it takes to compile but in the real world with bigger projects that that can be quite large it can be the the matter uh the difference between um a few seconds versus a few minutes in terms of actually building something and so normally you if you can you want to build a debug version for testing and stuff like that and when you actually ship it to end users you'll build a release build how do you build a release build um you can do cargo build dash dash and it takes slightly longer to do this in this case but we've now built a release version and you can find that inside of target release so now we have a release and a debug version here so normally you can you can do that that's fine but normally you wouldn't actually run your um project in this way um there's a shorthand for this and that's cargo run and what cargo run does is it builds your project and then goes ahead um and actually invokes the binary for you so so you can do that and now you've seen we've built it and we've actually run uh the binary so that's that's good to go there and this is kind of the normal flow of things normally what you would do is you'll code along your your ide will probably check things the vs code will check things for us we might run cargo check every once in a while and then when we actually want to run the thing we'll run cargo run to actually run it all right any questions so far in chat let me know um this is kind of an introduction to how rust uh runs the reason that i brought up a release builds for instance is a lot of times people get into rust they start building something then they run cargo run without the release flag and say wow this is actually not that fast i thought rust was supposed to be a fast language what's what's that all about well the reason is is because it's a debug build and those are typically much slower and once you throw on the release flag then things go much much quickly so uh that's an important thing to to remember it reminds me one thing that i wanted to talk about before we get into it is uh real real quick about why rust what is rust good for it because this often comes up it's important to know this and to keep this in mind when learning rust that rust is at the end of the day a systems programming language it's meant for kind of low level software think operating systems web browsers kind of foundational software um and while it is totally possible and sometimes a very good idea to ill to build like end user software um stuff kind of high level software on uh with rust as well um the real thing that rust as a language focuses on is low level programming and so today we might forget that at times while we're writing rust because sometimes rust feels like a very high level language it's got a lot of really wonderful features that are inspired by high-level programming languages oftentimes from functional programming backgrounds and so we can often forget that we're dealing with a systems programming language which is most analogous to something like c or c plus plus um but um as we'll often also see today rust is a a low level systems programming language and sometimes we have to worry about kind of low-level details that in other languages you can just forget about because they're they're not systems programming languages and that can oftentimes make rust difficult to learn because if you're coming from a javascript background or some other high level programming language even like java or c sharp you might run into something and say wait why is rust being so difficult here and the reason is is because it's a systems programming language and it has to worry about and care about these kind of high these low-level details all right so keep that in mind um it's a it's a low-level programming language we're going to be dealing with a bunch of stuff but as we've seen with cargo here sometimes we get some really nice features uh as well with rust alright so enough talking let's go ahead and start programming something today and welcome to everybody new in the chat um do let me know if you have any questions at any time even if they're not directly related to what we're talking about right now um feel free to throw them up in chat and i'll do my best even if uh it's not right to answer them right now we can go ahead and get them answered at some point uh in uh in the stream okay great so i'm going to go ahead and open up vs code and we're actually going to get started with programming here all right so here we are in vs code and we have rust analyzer putting up uh the run and debug things here but this is our code uh right here there's a question in chat about uh the clippy tool we probably won't talk about clippy today clippy is a linting tool um that's really great well um i think later on in another stream we'll get into more into tooling outside of just cargo and and rust itself um but for today we probably won't talk talk about that there's another question about does cargo provide built-in cross-compile support um can i invoke a single command to build for a different target so um for those who are unfamiliar with with cross compilation maybe you come from a language that doesn't do compiling at all let's just quickly define that term what cross compilation means is i'm on linux here windows linux a little bit confusing right but i'm technically in linux right here what if i want to build a binary that runs on macos or runs on a raspberry pi or runs on my refrigerator or runs on windows or something like that can i do that with rust that is called cross compilation and yes cargo is it's uh rust itself has built-in support for that um cargo doesn't really concern itself too much with kron's compilation um it just kind of relies on rust c the compiler to do it and the rust up tool that we saw that that handles different versions of the compiler um also deals with this so rust up we can add tool chains and say i want to compile for mac os on my linux machine and you can tell rest up to install the the standard library for for mac os and and bring in uh um support for compiling to mac os and that's all built in we won't get into how to do that today um that's definitely a more advanced topic but we'll talk about that uh later on in another stream but it is one one line of code or one line of on in your terminal to actually get that done all right so let's get started here what's the first thing that we want to do remember we're building a key value store here we're building a command line utility um we can go ahead and use um for instance uh some really great open source libraries for handling command line parsing and stuff like that but we're going to start simple we're going to start without any dependencies of any sort and see how to do this from scratch and then later on we can go ahead and add dependencies uh to make things nicer and handle more scenarios and things like that but we're gonna start plain and simple just by getting the command line arguments that are passed to our binary all right so how do we do that well we're going to use um standard and args here and real quick we're going to look at the syntax because of course the syntax might be new to you here um right here we are just invoking the standard module that's modules are just kind of namespaces for different things and rust so there's a standard module that's the standard library there's a module inside the standard library called imp for environmental type things and then args is a function here that we can call to get the command line and in fact russ analyzer is being very nice here and showing us the actual documentation for it saying that args is a function that returns the arguments which this program was started with normally passed via the command line all right great so and you'll notice here that if we go ahead and put that into a variable or better set in rust terms a binding here that rest analyzer i didn't write this colon args here but rust analyzer has put that here to show us that the type of args here is arcs is a a type called arcs all right now let me know if you're coming from a dynamic programming language if you've not done anything with static typing before because rust is very much a statically typed programming language and so that might be new to you and we can we can talk more about how that works in rust now in rust we uh the rust compiler will infer all of the types of our arguments here so we don't actually have to say that args here is um is a type called args uh rust will just infer it and in fact roast analyzer does for that for us and shows us uh here okay so we have our arguments now we're on linux and um if you're if you're familiar with linux you know that the the operating system pat when they pass in arguments to your program the very first argument that you get is the path to the program itself um which we don't care about we don't want to look at the program name itself we want to actually um you know for let's just say for now we're going to do something like a kb store hello world or whatever like this we don't care about this we want to get this instead so how can we do that well arcs here is something called an iterator in rust now iterators normally are covered kind of in a later on in the introduction but i think it's really neat to see it uh how it works in the very beginning what iterators are are types that allow you to iterate over them or get elements inside of them one by one so you can think of them kind of like collections where you can get the thing one one by one you can get the next element then and then the next and then the next over and over until there are no more elements and iterators are really great and that you can skip over the first um skip over the first element inside of the iterator using a method called skip so here we're going to skip over the first one so we've skipped over the first one and now args is the new type here skip args um and that is has skipped over the first element inside of of the iterator all right and uh chad is saying uh ryan the args and the arcs type and binding are different can we can we rename it yes so um just going back to how this was before here um we can call this we can't call it because it's a keyword um uh arguments so you'll notice here that we're calling this this binding arguments but the type is still args so argus is a type um and and it's always going to be args but we can call this whatever we want all right and of course arcs here is defined by the standard library so we can't rename that all right so and we'll go back here and do skip one now some people are saying that their their ide is not showing skip args here but instead this impul iterator type thing um if you have a different version of rust analyzer or something like that than i do you might see something different we'll we'll talk about this later on in the stream that impul iterator and is another way of specifying this here but but um for now just know um that uh the actual concrete type here is is skip arcs all right so we we've got our arguments we've skipped over the first one now we want to say that the key um that we're we're storing here this key hello is going to be the next element in our in our iterator right we've skipped over this one and so the next one is hello so we can go ahead and just say arguments dot next and that will give us the next argument in line here now here's the very first interesting thing that we'll see in rust arguments here uh is showing a red squiggly line with a uh with an error message here and this is our first error that we've encountered in rust and the important thing to do in rust when you encounter a compiler error is not freak out but to read the error message and take a look at it and what it's saying is we cannot borrow arguments as mutable as it is not declared as mutable all right and in fact if we go ahead and i'm going to open up my terminal down here instead let me go ahead and make this a little bit bigger for everybody um if we open up the terminal and run that cargo check command that we talked about before we'll get an even better error message here that that should be very helpful um for showing uh what is wrong here so it says even here help consider changing this to be mutable by adding me the mute keyword beforehand all right so this is a very important thing to know about when learning rust read the error messages oftentimes you'll find that they will have helpful hints to you about what what needs to be changed so what does this mean in rust everything is immutable by default you cannot change things by default you have to opt into mutability you have to opt into the ability to change things by specifying that they are immutable so this is this is different than basically almost every other language at least every other mainstream language out there except for perhaps functional programming languages in that in most languages things are immutable by default and you opt into immutability through something like const or something like that and it's the opposite uh in um it is the opposite in uh in rust now somebody in chat is saying that they are getting an error message saying that it doesn't know what args is you've probably uh you've probably gone ahead and written something like this let arguments args equals standard and args here and i also get this error message it doesn't know what args is that's because args is not in scope we have not brought args into scope here we'd have to bring it into scope which we'll see how to do that in in a minute um when i write when i write this this colon arcs here i didn't write that that's not something i wrote that's just something that rust analyzer is putting into my um into my code and in fact if we go ahead and uh show what this looks like just in plain text you'll notice there is no colon arcs down here i didn't write that that's rust analyzer so if you try to write that explicitly you can do that but rx has to be in scope it has to be available for use and we have not made it available for use um which we won't do right now we'll talk about how to do that in just a minute um so uh for now leave off the colon args and and don't put it there that's something just my my rust analyzer ide is putting in for us hopefully that makes sense let me know if you have any other questions all right so we have the arguments we have we're calling next and again rust analyzer is putting in this type here option string here so we didn't write this option string here rust analyzer is just putting it in for us to show us what the type is and it's saying that is an option string this this syntax right here of the of the um braces right here um is is uh generic programming and rust will we'll talk uh plenty about that coming up but just know that what we're getting back from next is not a string as you might expect in in other languages but an option string and why is that well just imagine if we go ahead and invoke our binary like this without any arguments what is the next argument um after this there is none right and so option and rust is a way of ex of modeling the fact that a thing might be there or might not in other languages where you're coming from um most like other languages you you have the idea of null or something like that like if you're coming from java and you see string in it as a type somewhere really it's not really string right it's either string or it's null well in rust when you see string it is always a string it's never null there is no concept of null at least not in in basic rest it's a very advanced topic um null so if we want to to say that a string might be there or might not be then we say that it's an optional string it's optionally there it's either there or it's not all right but for our intensive purposes we're just getting started and something like that we kind of just want to say i don't i don't care if it's not there if it's not there like i don't know just crash the program just just throw up you know it doesn't matter how do we say that well there's a great method on option called unwrap and what that does is it says hey if the thing is there then i'll just you know return it to you and otherwise i'll crash your program all right so let's go ahead and see what crashing a program actually looks like we can do cargo run just like this and if we're not passing any command line arguments here and you'll see down here thread main panicked which is the rest name for crashing when it tried to unwrap a none value and none is the optional way of saying the thing is not there option none is the thing is not there so it tried to unwrap the thing but there was nothing there and what unwrap does is when nothing is there it crashes your program all right now what does it look like when we actually pass something we can do cargo run and if you want to pass command line arguments to cargo run you have to do this dash dash here and everything else gets passed directly to your binary so we can pass in hello here and um it's built our program we're getting a warning here which we can talk about warnings in just a minute but but the program was successfully built and it was run and nothing happened because we're not doing anything let's print out the key here so the key is and if we want to interpolate and print line the syntax for that is this and we pass in the key like that all right so now we're going to print something out when we pass a key and here we go we see it down here the key is hello all right so this unwrap is a great way of just saying if it's there great if it's not crash the program and we can do the same thing for our value and just say that our value is arguments the next argument after after key and unwrap once again so here we can say the key is and let me put some single quotes around this just so it's clear what's going on and the value is so here we will print out the key and the value so and here uh let's clear this so it's easier to see i'm gonna run without a value and it crashes because when on line four as it says on line four it tried to get the value and the value isn't there and uh unwrap crashes when the value is not there but if we uh pass in a value here then now it says the key is hello and the value is world all right let me know if you have any questions about that this is this is important to understand about optional values about the fact that values when when when we say that key is a string here it means key is a string it's not null there is no such thing as null in normal rust so we know for a fact that key uh is a string here because we've already checked it with this unwrapped thing all right uh there's a question about option and versus result we'll get to that in in a minute um or later on in the stream but we we haven't touched result yet but we'll see that in just a second all right okay so now we've gotten the key and we've gotten the value now the thing that we need to to do is go ahead and store our key and value into our database so where's our database well i think the easiest thing for us to do is just write our database to the file system we're going to create a file and we're going uh to create a string from our key and our value and store the key and the value into this file on on disk all right so how do you actually create a file in rust well you know let's say i don't know how to to create a file where am i going to find out how to create a file well we come over um bugs.rs so if you go to doc let me type that one more time docs.rs slash standard here this is a shortcut when you go here to the rust standard library documentation and this is where we can find documentation about standard library types all right and i can just go ahead and here and type in file for instance um and here is um documentation about standard fs file which is a file type for doing various things on file now i i encourage you when you're learning rust to go ahead and try you know go through here read about these different examples that they have in here of how to do things this is how to read contents of a file and stuff like that once you get more used to the standard library you'll you'll figure out the best way to do things um i want us to take a look one step up at the standard fs module here because there are some really great helper functions here for doing certain things and in particular way down here at the end which we can make this a bit bigger way down here at the end let me bring this up for you here is this write function let's take a look and write what it does is write a slice as the entire contents of a file all right we don't know what a slice is this looks a bit gnarly here so we're going to have to figure that out at some point but at least we know when we look at the example for instance fs right here presumably it's writing foo dot txt to it's writing lorem ipsum or dollar sit to these different text files here alright so as with any language it's going to take a little bit of time to get used to the standard library to read through it to to know how best to do things but you are starting in a good place with this documentation and using this search bar up here we'll talk a little bit later on on other um uh other resources that you have at your ex at your um you know at your fingertips to to ask questions and things like that we'll talk about that at the end of the of the stream all right so we're going to use standard fs uh right um here to to write something to the screen now there's a question in chat that i want to talk about how would you show a more friendly error message of key or value is no can we use unwrap for that so instead of this is you know right here this is not the best error message in the world right um now there is um another uh method on on option here and i i want to back up to your question real quick just to be slightly pedantic here and i don't i don't want to pick on you or anything like that the question was can we show a more friendly error message of cure value as null just to to to put that in more rust friendly terms we should ask can we print out a friendly error message of cure value is none because there is no null that's not how we're representing the absence of a value in rust we represent that with option none so the question in a slightly more correct way to say it not to it's fine the way you asked it just um uh using terminology that is more akin to other programming languages is all um we should ask what can we show a better more friendly error message if the key or value is none um and and we can there's a really great uh method for that instead of unwrap we can use expect here and pass in a message to show like he was not there and there are others beyond this we won't get into them we can talk about those later on that can make you that can control this even more in a fine-tuned way but expect is something that i use quite often if we one run this without a key for instance you'll see here it still says this thread main panic here but now it shows key was not there instead of called option unwrap on none value so this is a little bit better if you want to show something even better than this um there are other ways to do that but we won't talk about those uh for now just just so we can move on from here but there there are definitely plenty of ways to um to control what happens if you get a none value here all right all right great so now we're going to go ahead and write our key and our value to a file all right and we saw that that was done through standard fs right here and what standard fs write expects first is a path and we're going to do kv dot db as our path just picking a random file name here kve db sounds like a great name and then we need to pass in the contents here alright so we're going to pass in contents here and of course it will complain and say what the heck is contents i don't know what that is right here cannot value find value contents in the scope it's not found in the scope like what the heck is contents well we don't have contents right right so we need to pass in some contents and we'll do it like this now contents here um what do we want to actually pass in well i think there's plenty of ways that we can do this but a great way a simple way to do this is for every key and value pair we'll write the key then a tab character and then the value and then each key value pair will be separated by a new line so that we end up with uh if i can write it down here whoops it's right down here our file you know this is what comments look like in rust file will look like this it's going to look like you know my key then the tab character my value and then a new line and then my key to tab my value 2 like that and then a new one all right so that's what we want our file to look like so let's go ahead and set contents to a string of some sort now how do we create a string we can use another macro called format here and what format will do is it's just like print line it uses the same syntax here of these curly braces but instead of printing to standard out it returns back a string to you all right so we can do format with a key tap character the value and then um format will i think no format does not append a new line so we can go ahead and do a new line at the end like this and then it's gonna say hey like you gotta i don't you gotta pass in i'm expecting two positional arguments here but you're not passing any arguments then that's totally true so we can pass then key and value here and so now contents here is a string with the key a tab character the value and a new line character here all right and we're passing that into to right here this this yellow squiggly line that my that my uh that vs code is showing to me here is a warning it it's something is not quite right which we're going to talk about in just a second but it's tot but it will run all right it will run fine so we're going to pass in hello world so we printed out the key is hello and the value is world and we can look here we have our kv.db file here let's print that out to the screen and you can notice here if we do something like xx x xd um this is just a binary uh a way of showing binary character here um you'll see you know h e l l o and then o nine is tab character w-o-r-l-d and then zero a is the new line character so it seems like it has worked uh correctly all right any questions about this so far we've written our key in our value to our kv.db file our database file and but we're getting this warning which we're going to look at in just a second there's a question in chat about what the difference between a function and a macro is we're not going to get into that uh too deeply for the most important thing to know today is that you can distinguish macros from functions and that macros always have this exclamation point here macros generate code so format will actually expand out to even more code than we have written ourselves whereas functions are just plain function calls just like you would expect in any other language so macros allow us to like create more code um in a way we won't be looking at those today that's another topic for another day but we can tell the the difference between a function and a macro just by the fact that one has um an exclamation point the macro has an exclamation point and function calls do not all right so let's take a look at uh at the standard fs right here and we'll see um you know this is this is all a bit gnarly here like this this p as ref and stuff we will not be talking about that today but this is uh this is a way of doing very generic programming so that your their functions can handle any like lots of different types of of input but the important thing to notice here is that right is returning this io result so this is where result types come in now let's think about writing something to disk when you write something to disk does it always go wrong right no sometimes it goes horribly wrong sometimes you don't have permission to write at that file path sometimes you know there are many different things that could go wrong when you're writing trying to write something to disk in lots of languages if something goes wrong what happens you throw an exception right well in rust there are no exceptions we've talked about panicking so far that's what happens for instance on unwrap panics are ways to basically kill the entire uh program so you don't you don't panic and then you don't throw a panic your program panics and it will crash and for our all intents and purposes today um this isn't 100 true um but for the majority of time you're never going to catch a panic panics do not get caught if you if your program panics it will crash all right but um writing to disk is something that we probably don't want you know if something goes wrong right so this maybe we want to recover from that or something like that for errors where there's some way of recovering um from them we don't panic but we instead return result types just like this io result type here and the result type is a type that shows either success it worked fine or an error of some type all right so let's go ahead and just write in here like um write write result all right so now we're we're storing that result in this write result variable here and you can see that uh rust analyzer is being very nice and showing us that it's a result of this funny looking thing that we're going to talk about in just a second or it's an error and the way that you can read that and understand that is that it's either going to be this funny looking thing here or an error of some sort all right now what is this funny looking thing here that i've been pointing at this right here is uh is an empty tuple it's known as unit and it is very similar to something like void that you would expect in in other languages unit is simply a um a type representing something that's not very interesting like if we write to um write to to disk here and it uh and it works what should we get when it works we should just get the like it worked but there's no there's nothing else uh along with that so this unit here is simply just like nothing it's void all right so it's either void it's either better said it's either unit or it's an error of some some sort how can we tell whether it's an uh how can we programmatically do one thing if it's a success case or um do something else if it's an error case well we're going to use something called pattern matching and what pattern matching is is if you're not from a functional programming background or haven't used some newer languages like swift or kotlin you may not have encountered pattern matching before pattern matching is sort of like um a switch uh statement from other languages but just frankly much better um and we'll see what that looks like in just a second here so we're going to to match here on our write result and now we're going to do one of two things we're going to say if it is okay which is the version of result that is like everything's good so result can be one or two things it can be okay or air if it's okay then we'll do one thing and if it's air we'll do another so you can see here what this is uh what this is showing is we we look at write result and we say okay if the thing is okay if it worked then run this code right here and if it's error then put that error that we have here into the variable e and then run this code so in a way this is sort of like try catch but match statements are or match sorry the match expressions are not just for errors in this case this is you can do this across many different types but this in in terms of result types is a way of doing something when something has succeeded or something has returned an error here um all right any any questions about this so far all right this is uh you know not too terribly exciting here um what we can instead of doing this we can go ahead and just do what we did before result just like option has an unwrap and an expect function on it so we're just going to go ahead and uh say hey if it errors just crash the program so instead of handling the error elegantly we're going to do once again just like we did before if it if it errors out if it returns an error to us just crash the program we're not going to handle it in this case any questions about results and pattern matching we're going to see it in just a second again in in action there's a question in chat about the format macro can be implemented as a function format cannot be implemented as a function that's why it's a macro why that is the case though is beyond the scope of uh of this stream for today we'll talk about that at some other time the short story is basically uh functions and rust cannot take a variable number of arguments um and format takes a variable number of arguments um whatever however many of these things uh there are in the format macros how many arguments it takes and that's not possible to do with functions only macros can take variable number of arguments great okay so um you know we've we've sort of uh you know done the the right thing here like we've we've run um our our program you know it's written to our our database here everything seems fine but of course this code is horribly buggy right um you know we we right now have a database that stores only one thing at a time we can't retrieve anything from it in fact if we go ahead and and run you know uh hallo which is the german version of hello world here and run that again and take a look at our database then of course our hello world has been overwritten with hello belt which is not a very um uh you know not a great way of using a database if every time you you add something to it it just overwrites what's in the database so we're going to next try to implement it so that we don't overwrite the the values in our database every time we add something to it and it looks like chad is already coming up with some ideas of how to to implement this that's great um sounds good we're gonna we're gonna look through and and explore some some different ways to do that i think the best thing for us to do right now um of course we could continue to write inside of our main function here and just do one big huge lung function or whatever but let's explore some other ways of writing abstractions and rust that will become very important as our program keeps growing bigger and bigger because we don't want to just have one very very very large main function we want to continue to build up abstractions around our code and i think the first thing that we're going to do is create a struct here which we'll talk about which trucks are in just a second where we do something like database now structs what are they um rust is is not an object-oriented languages there's no there's no idea of classes or anything like that and rust your types usually are structs if you've not used a a language that has structs before structs are simply kind of things that have uh named kind of key named elements inside of them um so you know we can have a field that's like you know field field one and it's going to be of type string and then we can have another field uh two and i field one and field two are just totally random names that i came up with they can be called whatever they want um and instead of a string field two is going to be um a u eight and u eights are unsigned 8-bit integers that's kind of the first look at the fact that russ is a low-level language we have we don't have int in and rust we have different types of numbers depending on how many bytes they take up in memory um so u8 takes up one byte and memory um it's kind of our first uh flavor of rust as a low language low level language here um so we can we can have a struct it can have fields with different types in them um we'll we'll not have any uh fields for right now we'll we'll get to that in just a second but we're gonna have a database type um and and in rust you don't say that database is a class a database is a struct and what we're going to want to do is create our database and uh and we'll we'll go ahead and create our database through a constructor of sorts now i don't i'm hesitant to use the word constructor because rust doesn't have constructors as a as a language concept but we'll see what that means in just a second there's a question in chat can you do private public fields like uh some other object-oriented languages yes rust has uh visibility of its fields by default everything is private and then you can add the pub keyword beforehand to make it public um so we'll see we'll see what like it's a little bit different than a lot of other languages because private fields are still viewable by other things if you're in the same file basically so we'll see what that looks like in in a little while all right so we have this struct database that's all well and good but how can we can we add some functionality to it um you know we've seen up here that we have i've used the term method before there are these methods how can we add methods to um uh to database and the answer is yes the first thing that we're going to do is create an impul and this is the way of adding functionality to our type so unlike in other languages where you might expect something like okay let's add some some methods here that doesn't work like that and rust and rust the syntax is you create the struct with its fields in it and then somewhere else you have an implementation and that implementation is where you add the methods and uh and the associated functions with that type all right all right cool so what what we're first going to do is create a an associated method that constructs our database type so how do we do that well inside of our impul we're going to create a method a function called new and that new function has a return type and that return type is database here so here we've created a function called new it returns a database not very exciting what it does because database is not very exciting and how can we call this new function well if we want to do that up here we can say database colon colon new and now in here we have a database [Applause] now um oftentimes you i will refer to this as a constructor here but there's nothing special about new we could have called this create not create create we could have called this foo we could have called it whatever we wanted we pick new because new is a convention here for kind of construct date constructor-like functions um but we could call this function whatever we want um there are there the idea of constructors as a language thing like you have in a lot of other languages is there that doesn't exist there is no new keyword or anything like that in in rust this is simply a function called new that's associated with the database uh struct all right great so we we've created our database here um but perhaps uh perhaps what we want to do instead of just creating a database here is we actually want to store our our values inside of the database um there's a question in chat about when you create a struct is it a reference that's getting we're getting a little ahead of ourselves um for those coming from a c and c plus background or languages where you have to carry a care about stack versus the heap and stuff like that structs are stack allocated and so when you create a struct like this it is all limited this is all happening on the stack here so this is not a reference this is not a pointer or anything like that um this is living on the stack um and somebody else in uh in chat is saying coming from c plus i'm not a fan of the new name it implies allocation it might imply allocation in c plus but new does not apply allocation at all and rust so different language different naming conventions um new does not imply allocation and rust whatsoever um we'll talk a little bit about stack versus heap because i know there's probably a lot of people watching that are coming from languages where you don't really have to care about the stack versus heap so if you're if you're coming from that background and are not sure what we're talking about here don't worry we'll get there in in due time so our database we wanted to actually store our key values in memory we wanted to keep track of our key values in memory so that we can add to our key values and then maybe eventually at some point in our program like say hey all the values that you have in memory write them out to to disk for us so our database needs to keep a hold of keys and values is there a a data structure that people can think of that is good for keeping track of keys and values well i can think of one it's called a map or a hash map and luckily in rust we have uh the standard library which has um a hash map implementation um so here we can have a field we'll call it map but again map is whatever you want to call it you can call it whatever you want and inside of standard collections hashmap is our hashmap type and i'll erase that just for a second just to show you what happens when we do this we'll get an error message here which got to scroll to the end here to see the error message and the error message is saying hashmap expects type arguments that's because it's not good enough in rest to say that you have a hash map you need to say a hashmap of what what are the types of keys and values that your hashmap has and someone is asking a hashmap is what they what c calls a dictionary yes that other languages call hashmaps dictionaries some of them call them maps there are various names for this these are just key value stores arbitrary key value stores objects i think is the word in javascript you would use for this all right so we need to show what the actual type of our keys and our values are our hashmap for for our intents and purposes for today is going to contain keys of type string and values of type string all right and this is just the syntax for for generic programming these are generics here so hashmap can contain keys of type anything but for this particular hash map we're using keys of type string and values of type string now i know we're going to be writing standard collections hash map several times we're going to be referring to hashmap all the time and i don't want to have to always write standard colon colon collections colon colon hash map that's very tedious can i go ahead and just call it hashmap and the way that you do that is to bring it in the name hashmap into scope um and we've already seen this before that we wanted to to do this with the args uh type before this is how you you do it you say use and then standard collections map and hash map and now hashmap can be referred to um just by the name hashmap we've kind of brought the name hashmap into scope here so we can do this instead [Music] there's a question about new versus default we haven't talked about default so we won't go too deep into that there are some differences in convention there new is normally for just creating it you can take new can take arguments for instance because it's a function default doesn't take any arguments so it's the default value for database we probably will have a default implementation but we'll get to that in in due time and there's another question about type defs and type aliases in rust yeah you can do type defs and type aliases we'll talk about that in a little bit but we won't do that just for now just because i don't want to throw too much at you all at once um great so our database has a map in it and you'll notice now this is complaining and saying hey i'm missing my field map can you give me a map field sure we can so then here we say that map is going to be a hashmap and you'll notice here that hash map has a new function on it again convention um you know we called this new because it's convention and and the standard library called their their constructor function new because of convention they created the convention and so now our database has an empty hash map inside of it all right so this is all well and good [Music] uh you know we have our database we've created it um it's it's got an empty hash map in it but you could argue that what the new function should actually do is go ahead and read from our database file and populate that hash map with um with the elements from that file so parse the database file and store the elements inside of our hash map so that's what we're going to be working on now so the first thing that we're going to want to do is is actually there's a question why colon instead of equals after a map i i assume you mean here um because that's the syntax they chose i i don't know if i have a better answer than that um yeah that's just that's just what the syntax is um it's the same as when you declare as when you actually instantiate all right so what we're going to want to do here and we can write this out in some kind of pseudo code here is we're going to read the kv.db file here and then we're going to parse the string and populate our map all right so let's go ahead and read the kv.db file then we already looked at standard fs here for our write function um turns out that they have a read function as well um this returns a result and uh real quick uh standard i o result is just a specialization on the gene like the normal result type where the error type is just set to standard io error we people were asking about type def's type aliases and rust this is type aliasing and rust here so we we have seen it uh in fact so standard i o result is just a result where the error type is standard i o error all right and the reason that they return that is because for all these operations the errors that you get are i o errors they're input output errors right so this read function here returns back us a vector we don't know what a vector is of of u h that seems like we can we just get a string like return to us a string well in fact we can here is read to string which is just like the the normal read function except it returns a result of a string all right so this looks like something better that we want to do let's go ahead and do standard fs read to string and we know that our database is at kv.db and just like our other functions from standard fs before this returns a result as we know and so it's complaining when we don't use it and saying hey you should use results you should at least acknowledge rust kind of encourages you to acknowledge the fact that things can fail now as before we can go ahead and and unwrap just like this like we did before but i think what we can do instead is actually see how returning errors and rust actually works so um we talked about pattern matching before where we could match on the thing and we know that uh results can either be okay and in this case registering returns either okay with the you know the contents string that we had and we can run this code if if we get back okay the thing worked or we get back error with the error and we can run this code if it errors out all right so what do we want to do when we error out well wouldn't it be great if we just like return back that error to the caller and say hey caller you you handle the error like this function will just return the error back to the caller we can do that with the return keyword here where we return error now you've noticed before with return returns like we haven't used return before that's because in rust there is return the return keyword but you don't have to use it at the last as the last thing um in the scope here so just there's implicit returns if you want to return early then you use the return keyword all right now we're getting an error message here it's saying hey like you said that this new function returns a database i expected you to return a database but i found a standard i o error instead like what's going on here that's totally true we say this thing returns the database but we're trying to return an i o error here instead that that can't be right that's because we need to return a result here where if it's if everything's good you get back a database and if everything is bad then you get a standard i o error here now um it's still complaining because it's saying hey like you said that you return a result but you're you're returning an io error and that's that's true we have to return back a a result in the error case here like this so we're returning back a result in the air case with the specific error that we got and and rest um result is such a common type that actually you don't have to write result calling holding here you can just reside or you can just write air like that all right so we've handled it so far where if we get an error we bind that error to the name error and we simply return from our function with error error a lot of mouthful but hopefully this is clear so what do we do if everything is okay everything's good well we have uh we have access to the content string here it would be really nice if we could use the content string out here can we somehow like bind a variable now a lot of people would say something like can we do something like this maybe like let's see and then c equals contents this is maybe something you would try and do um in in other languages and in fact this works uh in rust this is fine to do it's not very idiomatic but this this will compile you don't actually have to do it like this because match pattern matching in rest is an expression not a statement so you can actually whatever is returned from this scope right here is what is what is the value of the entire match expression meaning we can actually bind to it and say like our contents are equal to whatever we return back from here so um and i'm going to change these names real quick this is going to be c this is going to be c and this outer variable will be contents so what we're doing here is we're matching on the result of read to string and if it's okay if it's equal to result colon sorry result calling calling okay which we can can just go ahead and write as okay because it's already in scope if it's okay then bind the the contents to the name c and then return that seed and put it into the the binding name contents here so now from here on out we have access to contents all right now if you're not used to a functional programming language or where this might be a normal thing to do where you have match pattern matching which is an expression this might take a little bit of time to wrap your head around but almost everything in rust is an expression not a statement meaning you can you can bind for instance if uh expressions to variables just like a tern area operator in other languages you can do that regular if expressions are like that pattern matching is like that things like that any questions about this there's a question in chat about rust is rust uh a functional programming language um i would say that rust is a uh is a an imperative programming language in the same vein as c as c with heavy borrowings from functional programming so it has functional programming concepts running deeply through it but it is at the end of the day not a functional programming language oftentimes when you kind of have to choose between the two you'll end up choosing imperative programming because rust is a systems programming language it really does matter at the end of the day kind of what's happening on the machine and sometimes you have to poke through the the high level functional programming concepts to get there um so does it always force you to air to handle errors um in a way yes like if we if we didn't do this here we would get the the warning that we had the yellow squiggly line now our code would still compile but we would get a warning saying hey you're not handling something so it's not necessarily forcing us to but rust is definitely trying to hint that we should do it um it is much harder to forget to handle errors and rust than basically any other language with the exception of again functional programming languages all right so this pattern here of like matching on a result um you know taking in the okay case like taking the thing from okay and binding it to a variable and in the error case just returning it back from uh from the function is so common in rust that there's actually a shorthand for it so we can take this and instead of writing all this go ahead and comment that out we can just say let contents equals standard fs read to string with kv.db here and we can do question mark and this question mark is a is a piece of syntax that basically expands out to this up here because this is so common and in fact this question mark didn't exist in and rest 1.0 it was introduced later on because as a way to kind of um encode this pattern in the language so this syntax right here is exactly not exactly but morally equivalent to this right here any questions about that this is error handling uh kind of 101 and rust here and as you become used to to rust here you'll you'll very easily see when you see a function you'll see the question mark at the end and say okay if there's an error it will just bubble up to the to the caller great now question mark here and i believe there's a there's a um there's a question related to this is question mark like always with result or can you use it with other types question mark is not only for result but for today we're going to think of it as as working just with the result but later on we'll see that that's not always the case um and there's a question about question mark being equivalent to public void foo throws exception well this is not on the this is this right here is the is the functions declaration right uh this is is sort of um an acknowledgement that something returns an error in the error case like if there's an error here an error will be returned from the function read to string there's a question if the file isn't there does it handle that case you can see here errors the function will return an error if path does not already exist so if kv.db is there it will return an error and in that case and in fact let's uh let us go ahead now we have in order to get this i'm going to get this to compile real quick we said we're returning a result here now we're returning database so saying hey you're trying to return a database but i'm expecting a result of a database we have to wrap this thing in okay and say it's a result okay so now this this uh this uh returns a result here this database new is a result here and just like before we can say like uh sorry expect database new crashed so if we go ahead and run this if we comment out writing our database and delete our database kb.db and run and of course we have to pass in our key and our value uh sorry about this let's get this so we're going to pass in our key and our value here um and i should save the file that's why it didn't crash sorry clear here um oh why is this interesting um i thought oh did i not oh kvw db is still there sorry about that i was very confused kbwdb got created again we've just removed kv.db so we see here kv.db is no longer there let's run it one more time we're not creating kb.db and now it crashes and here is the the error message that we get back out here database.new database new crashed and here is the kind of os uh error here the kind is not found no such file or directory now there's plenty more we could do here to make this even nicer to show even nicer error message and stuff and things like that but that's not part of today's stream but now we've seen that um how it's working we came down to read string right here read string um returned an error because of the question mark we return an error from the new function and then on expect here it crashes the program and in fact if we go ahead and remove that expect here and run again it doesn't crash because we never explicitly say crash on error ex the dot expect is what actually causes the air to crash this is an error here but we don't crash the program because um returning an error is fine it's we're just returning a value that represents an error we're not actually uh we're not actually causing the program to crash in any way hopefully that's clear if it's not please please please ask more questions um we can go through that again but now um what we have here is um we're reading from our database we're getting the contents of the of the string [Music] what we can what we can do in fact is say we can do let's not do that we'll handle we'll handle the error case of uh if kv.db is not actually there um in just a in just a bit but for now we will assume that kb.db is there and in fact for now we can just go ahead and write to it of course this is not how we want this to actually end up running in the um in the long run but um this will make a development a bit easier um yes so uh there's a great question um in chat and i think this is the perfect time to dive deeper into what what is result actually like result is result is either okay or error chad is asking is result like an enum but it has like types with it and the answer is absolutely that's exactly what it is so let's actually take a look at result uh here the regular result and result is an enum so we haven't talked about enums yet this is the first time we're encountering enums but enums are again like enums from other languages just better our result type here is an enum with two variants on it the first variant is okay that we've seen and the second variant is error that we've seen and this result enum is generic on two types the type that gets returned with okay and the type that gets returned with error here so and enums and rust are kind of morally equivalent to enums in other languages and that there are variants that it can be but unlike in a lot of other languages enums and rust can have data associated with with each uh enum yeah and the term discriminated union is now getting passed around that's that's exactly right yes um in c often if you're coming from a c background you might have represented things in this way using something like a tagged union under the hood enums and and rust are are very similar to tagged unions except that the language doesn't allow you to um screw things up so if you if the result is okay you can't accidentally read an error from it that's not possible there's no construct in the language that allows for that and if you if the result is an error variant there's no way to accidentally read out the the success value so um enums are kind of like uh these tag unions but without any of the dangers of accidentally using them wrong hopefully that answers the question there there was a question of what it will write overwrite the old file i think we've seen this already write just simply overwrites the file here this function will create a file if it does not exist and will entirely replace its contents if it does which is not what we want but that's ultimately what we're going to try and fix with this database implementation okay so we've read the contents from our kb.db file we have them in a string here how can we go ahead and actually parse this this string well the first thing that we want to do is we said that each key value pair is separated by a new line so the first thing that we can do is say contents and then use this lines function here to give us another iterator just like our args iterator from before that allows us to get each line in turn each line of the content and lines handles things like carriage return and on windows and and things like that so it's a little bit smarter than just separating by the new line character um but it gives us each line of content and we we can use the for in construct and rest to iterate over them so for line in content dot lines here and now a line will be yielded to us each time now astute uh listeners and viewers will notice this ampersand stir here which we've not seen before we'll talk about that in just a minute this is this is going to be our introduction into uh into rust's borrowing system so hold on just a minute for that so here we're getting the line each line and turn and what do we want to do here well each line is going to be composed of a key a tab character and a value so we can say line dot uh split once i think is the best one here so split one splits the string on the first occurrence of the specified delimiter which sounds good to us we want to split once here and the delimiter is going to be uh the tab character here and in rus single single quotes are characters um uh which we'll talk about in just a minute characters and double quotes denote a string um so you can't write more than one character inside of a inside of the single quotes it will it will complain with you okay what's it saying here yeah that's fine so split once what did split once return to us split once returns an option of a tuple okay so this is this is another interesting return type here we know what option is option denotes that it's either there or not which makes sense right like splitting once maybe doesn't return anything you can see down here in in the examples cfgs splitting once on the equal sign returns none so we can uh we can just go ahead and write expect here and like honestly if the if the database is corrupt where the the line doesn't contain tab character let's say like for now we'll just say corrupt database and we'll crash we can we can eventually handle this better um where we don't uh crash when we have a corrupt database maybe we can somehow recover from that but for now when we split on the thing and there and it's not there we're going to crash the uh the program all right um and so now we get back our pair and our pair is a tuple now tuples are just kind of uh they're sort of like um sort of like arrays but they only contain like a fixed set length of of things and they could be of different types this this happens to be an array of this ampersand stir which we haven't talked about two times but they could be different types and different tuples allow for different types unlike arrays so we don't know what ampersand stir is quite yet but we know that our pair is going to be um both the prefix it said uh it splits once and it returns the prefix and the suffix so this is going to be the prefix and this is going to be suffix and i believe with with them removed yeah so that the equal sign is is removed in this case so our tab character will be removed now there's another way we can write this where we can we can deconstruct this and say that we're going to put the prefix into a binding called prefix and the suffix into a binding called suffix here and in fact prefix is not the right word key and value are better names for this what's it still complaining about here ah no that's so sad this is a good chance for us to we can't use uh split once because it's not stable so you can see here it's actually a new feature that hasn't made it into the standard library quite yet you have to opt in to newer unstable features in order to get it this is a part of how rust evolves itself over time is by adding new features in the nightly compiler that is not quite as stable as the stable compiler which has very strong stability guarantees and you can opt into that if you want to so split once is not available to us because we're only going to use the stable very stable compiler which is which is sad but i believe there's a split n split end that we can use instead is that not yeah split in split n does uh what split in does is it's an iterator okay so we're going to get an iterator back here an iterator over substrings of the given string slice so it will if we can see down here um where mary had a little lamb where we split three times on the space character we'll split it on mary had and then that's that's all this uh space characters that it splits so um so uh so it returns back this third piece so it's not splitting three space characters but rather into three chunks so we want two chunks here or we're going to split here and expect doesn't work anymore since this is returning an iterator so that that split once was was really nice but unfortunately we won't be able to use it in our code today okay so this is giving us a an iterator here um of of all the chunks um the two chunks that we have that are split here on tap characters um we can we can do what we did above of uh of saying you know this is our chunks iterator here and we can go ahead and say that our key is going to be let key equals chunks dot um next here um and of course just like above we're we're iterating through we're mutating chunks as we go along when we get the next value so we have to declare that as mutable and again we can just crash the program if we don't have a key like no key here and we can do the same thing with value no value this is totally fine so again we split our line into two chunks we've called next on it to get the key and value next again to get the value and we're just going to crash and thanks for joining see you around all right so now we have the the key and the value and what we can do is we can insert our key and value into our hash map so we're going to bring up this hash map to the top here and say like let map equal now we're going to be mutating this map as we go along we're going to be inserting our keys and values as we read them from the from the file so we need to mark it as mutable here and we're not actually inserting anything into the map here so we can do that with this insert uh method here which inserts keys and values into our into our map so we're going to insert key here with value all right so what we've done here is we've read the string from our file we've broken it up into lines for each line we've gotten two chunks split on a tab character and we've gotten the the first chunk here and if it wasn't there for some reason we just crash the program and if we get then we get the next chunk and if it's not there we crash the program as our key and value and then we insert those keys and values into um uh into our map now there's a question here what if the key already exists well insert actually re uh insert here um returns back an optional value here meaning if you insert into the into the thing into the map and the key already exists it returns back the old value at that key and returns back none if it's if it was the first time so the question is i don't know what should we do there what's the right kind of business logic um in this case i think for now the best thing to do is just ignore it we're just going to ignore it and we'll override it there can be duplicate keys and the last one wins we can handle that in a more elegant way later on all right this code does not compile though if we you know we there's red squiggly line there if you didn't check uh catch it we can go ahead and and run it here cargo check again and see it for for real here and saying the map that we're trying to put into our database here is the wrong type we expected to put in a whoops we expected to put in a hash map of string to string we found a hash map of this ampersand stir ampersand stir like what's this all about what are these amper stirs things we should probably finally talk about that and this is where we're going to get into the idea of borrowing and rust um there's a question here instead of calling dot next uh each time is it possible to deconstruct um so the thing about iterators and rust is that they're lazy so when we have chunks here we actually don't have those elements already in chunks all we have is a type that kind of represents the possibility of us retrieving those eventually so this is not a collection in the sense that in memory there's there's our are two chunks sitting there um this is not a this is not an array or or a vector or anything like that so next actually tells the iterator like retrieve the next item and next again retrieves the next item because our iterator is lazy it doesn't do it uh kind of by default so there's a way for us to [Music] eagerly like collect all of our items into a collection which we can do that um and then we might be able to to deconstruct our elements from that collection but our our chunks iterator is not a collection uh yet we'll talk later on about how to turn an iterator into a concrete collection but not not for now okay so now we want to figure out what this star ampersand stir thing is actually all about what we see here that line here is an ampersand stir this these key in values are an ampersand star is like what is that does this have to do with strings the answer is yes this has to do with the idea of ownership and borrowing one thing that we haven't even really encountered yet is that rust is a is a systems programming language and therefore does not have a garbage collector so in rust we don't have a garbage collector all always looking at our items and figuring out when they're not used anymore and garbage collecting them away in most other languages um out there basically every language that really gets used nowadays with the exception of c and c plus plus you have a garbage collector of some sort you have garbage collection of some sort even in things like swift uh which or objective c um you still have garbage collection there you have some run time that is keeping track of how uh how many times the value is used and when that value is no longer used in your program the garbage collector comes and sweeps it away russ does not have that and rust we have the idea of ownership we're going to look at at that and with this key here which is an uppercase string the question becomes we have this string and this string is represents data somewhere in memory and when this data is no longer used we want it to uh we want it to be cleaned up we want that data to be reclaimed now in a garbage collected language the garbage collector would do it for us in c or c plus normally what we would do is we allocate a string into into the heap and when we're done with it we call free on that and that item especially in older c plus plus this was definitely the case and then see it's still the case in rust we don't actually ever really end up calling free on an item what we do is say that each uh each variable that we have here each binding that we have here or each item that we have in memory has an owner so this string here is owned by key now what is what is ownership means what ownership means is that when that binding goes out of scope which key here will go out of scope at the end of main here the memory that it points to the memory that it has not that it points to the memory that it has ownership over the string here will be destroyed it will be and rust parlance dropped and so automatically what the compiler determines at compile time is that key owns this string key goes out of scope at line 12 here and so the string will be dropped it will be cleaned up will be freed at line 12 here same with value value owns this string value goes at scope on line 12 and it will be dropped then freeing up the the uh the strings memory and then kind of underneath the hood what that is is just a call to the the allocator to deallocate that memory we can we can always tell when things are going to be freed very easily this hash map for instance this map binding owns this hash map the map binding goes out of scope at line 38 here that means this hash map should in theory you would expect be freed at line 38 but it doesn't why because in rust you have you can transfer ownership between bindings we call this moving elements so a hashmap doesn't go out of scope here this map doesn't go out of scope here at 38 it gets moved into this database this database struct here and the database struct then gets moved out of the the out of the new function so this hash map gets moved into the database the database gets moved out of the new function and then the database ends up in this database variable here which then goes out of scope finally at line 12 and that's when the database will be dropped it will be freed and its hashmap that it uh owns will be freed and the hashmap strings that it owns will be freed as well so the first thing to to get your head around with ownership and rust is when you look at a variable you sh or when you think about a value in memory like this database you should be asking yourself in your at each point in your program what owns that memory so again for a hash map here what owns the hashmap is this map variable it gets moved into the database now the database owns the map and then this database variable owns the database struct and it finally goes out of scope at 12 without being moved somewhere else and that's when it's destroyed there's a question about reference references that's what we're going to get to in just a second all right so now let's get back to our uh this contents string here we when we call read to string here we're reading into this uppercase string here another way to refer to string here is is an owned string the string owns its its memory and when contents goes out of scope which happens here at the at the end of 38 it will be freed all right but you'll notice when we call contents dot lines we don't get back strings here for the lines we get back ampersand stirs now what are ampersand stirs those are views there are there are references there but there are views into that own string so this line here when this what does this own well it owns a reference but that reference which is just at the end of the day pointing to some point in memory doesn't actually own anything so when it go when it gets dropped nothing happens and this line is literally a pointer pointing out referring to other memory specifically the first line the second line and the third line and so on uh that uh is owned by contents here so unlike in other languages where you know if you're writing ruby or something and you have a string and you call lines on it it's probably just copying that that data and giving you a new string object that you can that you can do whatever you want with manipulating however you want that's not what's happening in rest when you call dot lines it is just handing you references the end of the pointers that are pointing up to the first line so what is line kind of uh in in memory actually it's really it's really just a pointer um now if you if you know rust it's actually a fat pointer but we won't talk about what that is uh exactly today but really it's pointing up to the the each line in turn and it's the contents the contents binding that owns the string and when it goes out of scope it will be dropped now the the this is not too dissimilar from c plus the difference with rust and c plus plus is is that in rus it's guaranteed that this line that's pointing up to memory that contents owns cannot be used after contents has been dropped so once contents is dropped we can never use line and in fact if we try to use line after contents is dropped uh it it won't be possible now syntactically here it's not really possible so there's not really a good way to to show this um but we'll see more examples of this as we go on this is kind of the core foundation of ownership now if this is new to you this may be a little bit confusing it might not be 100 straightforward we're going to be looking at this over and over and over again so don't worry too much about it just know that when you see this ampersand stir here it's not the owned string when this goes out of scope it won't be dropping the memory of the string it's really just a reference of view up into another's own string and that's exactly what we're what we're you know getting when we when we're we see this error message here what it's saying here is i expected a hash map where the keys are owned strings and the values are own strings but what you're handing to me is a hash map where the keys are these string views string slices as we call them and the values are also string slices the hash map wants to own the string memory it doesn't want just views into some other into some other memory now chad is saying we could probably show that by adding some drop calls in there that's that's true but i just i don't want to i don't want to confuse people too much with with putting and drop uh too soon so just to kind of reiterate this to to um to kind of start building up your intuition here at a type level like this error is simply the hashmap is a different type we we're expecting a hashmap of a certain type and we're handing it a hashmap of another type but underneath that is why do we have these ampersand stars here instead of these own strings it's because these keys are really just pointing up and looking into this content string here what we really need to do is instead of handing our map this these views we need to say i'm going to give you your own string that you can own in here now how do we do that well there's several ways that we can do that the what we really want to do at the end of the day is copy the memory and say the the the data that key is pointing at we just want to like literally copy it say it's the memory there and on the heap we're just going to take it and put it put the same exact memory somewhere else have two copies of it and the best way to do that in this case is in my opinion is calling to owned so we take this borrowed value this thing that is not owning its memory and calling to owned on it in order to get an owned string we're just we're gonna copy the memory over and now we can see that it compiles just fine because we have copied key and created an owned value here now there's a question could you explain the difference between copy and to i think maybe you mean clone because there is no copy method like if we try to copy here it will say i don't know what you're talking about there is a clone method here and clone if we if we look at that also it doesn't work what clone does is it literally just clones it duplicates this view so we have a view into our our content string we call clone on it now we have the second view into the string so we can have multiple views into the string but we can only have one owned value and there's a there's a question now about using string from so there are multiple ways of getting of going from a string view a string slice i should i should use the proper term string slice to an own string string from is one of them um string from is totally fine it's it's more to type uh is really the uh the best answer i can give you they're basically different ways of accomplishing the same thing from different from different uh i had one yeah different traits which we haven't talked about traits yet but uh but we will so at the end of this you're not i'm i don't expect anybody here to to master the idea of ownership here we've only really just begun we've only really just uh you know began to look at what ownership is the important thing to remember is that every thing and memory has an owner and we can you can transfer ownership also known as moving one way to move is by putting something into a struct another is to return it from a function you can move things and transfer ownership then but when the owner goes out of scope without moving its memory somewhere else that is when the value gets dropped meaning it gets freed and we can have references to things which are just views like pointing at something and looking at something else in memory those things don't own their memory they're just looking at it we're going to see as we go forward that rust always ensures that if we're looking at something with a reference for just viewing it with a reference we can never view something for longer than that thing lives meaning if the thing goes it gets dropped if it goes if it gets freed we can't have references outstanding references looking at that thing in c and c plus plus this is known as a dangling pointer dangling pointers and rust are impossible and safe rest you can never have a reference to something when that thing is no longer valid when it has been dropped there's a question so a cloned reference is a reference and so if you clone a proper valid yeah so here when we called clone here we called clone on this reference this ampersand stir reference we've just got a new reference we've cloned it if we were to call you know let contents clone equals contents dot clone here this has literally cloned the content string we now have two sep entirely separate strings that happen to have the same uh content they don't have the same con they don't have the same like things in memory but literally the bytes have been copied over to a new place in memory and so now there are two versions of this string in memory and now they can have separate lifetimes you can you can contents can be moved somewhere else we can drop contents clone earlier later it doesn't matter some people are asking um they've seen two string here as another one two string is also fine um it it's a bit confusing because you're going from a ampersand stir which is a string like it's a it's a string it's a view into a string and you're calling two string on it i always found that a bit weird but i mean it makes sense you're you're going from ampersand stir to a string so and it works fine this totally compiles this is just another way of doing it the reason that there are so many ways is because the there are um two string is implemented for a lot of different things not just ampersand stir two owned is also implemented for different sets of things so these are like different methods for accomplishing the same things that in the case of ampersand stir happens to do the same thing as two owned so two string and two owned in this case do the same thing but in other types there might be other types where two string uh exists and two ohm doesn't or vice versa or something like that so in this case they're basically equivalent to each other you can pick the um i believe two string used to be slightly less performant and that it there was more machinery it had to go through in order to accomplish uh you know actually reallocating the string um i don't believe that that's still the case though so pick whichever one you think is clear to you or i i stick with two homes though okay so we've we've parsed the string we've read the we've read the kb.db file we've parsed the string we've populated our map our map is now a part of our database here um we now have our database here we can again call expect here to to you know uh creating db failed so when if this returns an error we're just going to crash that's totally fine um i think we're gonna end it here for today in a somewhat unsatisfactory place in terms of our business logic here our thing is totally busted right like we're creating this file uh every time with these contents that get overridden then we create our database to actually parse that thing out and we're just going to re-recreate this every time the next time that we we meet for the next version of this what we're going to be doing is fixing this up so that we handle the case if the if the database doesn't exist create it otherwise parse it out and read it then we can start adding methods to our database file here database struct here in order for adding new keys and values for getting keys and values and things like that that we can use in our code i want to stop uh here for now and just finish up if there are any questions from people about the code that we have here today more than happy to answer those and i also want to to leave you with a little bit of optional homework um if you if you want to do that um in order for in order for us to check whether the database already exists a great thing to use is path buff which is simply a a type that represents paths on a file system and one thing that exists on path buff is exists so you can and you can read a little bit about the difference between path and path buff but exists checks the file system to see whether the file exists on disk so a little bit of homework is to create a path buff for kv.db and then check if it exists and if it doesn't exist create it it's a little bit of homework for for you if you choose to do that that's what we're going to start with next time also don't forget to check out the rust programming language book i would definitely recommend reading through chapters one through four and you can start under understanding ownership as well is a really great thing we're going to be getting into ownership a lot more next time that's a really great thing to do i would also like to point out um if you go to wrestling.org here under community there's really great uh places here for finding user forms internals forms for internal development and chat platforms in particular are the most interesting the discord is probably the best place to go for asking your questions um you can feel free to reach out to me but this is a great place to also ask your questions in a chat format and the last thing that i'll ask you to do is um if if at all possible please subscribe uh here on my twitch twitch.tv slash ryan levick where you are right now if you're watching this live on my youtube you can search for me ryan levick on youtube um and please find you can see all this up above at the top of the screen my github my my twitter handle and my my youtube please search for me there and follow me there let me know what you think about this it's very important to me to know that people are enjoying this because that gives me the motivation to keep doing it so if you do like it please let me know if you don't like it please also let me know and and you know we can keep doing this in the future um there's a question of why only one of the discord channels for us are linked on the rest one of them is the official discord um that is i i believe like actually monitored by people from the rust uh community or the the rust team um on an official level the other one is an unofficial one um what the difference between the two actually is in practice i have no idea so pick whichever one makes you feel more comfortable if you like the idea of community driven moderation i the only one i'm sure is moderated is the official one that's linked here the other one might also be moderated but just in a different way not sure all right so if there are no other questions i really appreciate everybody's attention i hope that you learned something today again this is not the last one that we're doing so if you're still unsure about something um don't don't worry we're gonna keep going and figure out more about this code take a look at this on youtube afterwards to understand to watch this again read the code and most of all try it run it break it see what happens when you move things around to to get those error messages and and try and read through what those error messages uh are like i'm hoping that stream part two will be next friday um i might do a different stream next friday but they will it will either be next friday or the friday after that so stay tuned all right thank you very much and i will see you all around and i'll be hanging out on stream a little bit afterwards as well so thank you bye
Info
Channel: Ryan Levick
Views: 3,231
Rating: 5 out of 5
Keywords:
Id: WnWGO-tLtLA
Channel Id: undefined
Length: 124min 4sec (7444 seconds)
Published: Sun Nov 15 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.