Check out a free preview of the full Polyglot Programming: TypeScript, Go, & Rust course
The "Submarine Rust Solution" Lesson is part of the full, Polyglot Programming: TypeScript, Go, & Rust course featured in this preview video. Here's what you'd learn in this lesson:
ThePrimeagen walks through translating the TypeScript solution into the Rust version of the solution to the submarine navigation problem.
Transcript from the "Submarine Rust Solution" Lesson
[00:00:00]
>> All right, everybody ready for the greatest of them all, rust? Yeah, yeah, yeah, okay, so I'm gonna do the exact same thing. I'm gonna go to projects, I'm gonna make my dir rust. We're going to keep these three dirs for as we're building the CLI program as well.
[00:00:13]
We're just kinda pre-setting them up. And of course, I'm gonna go into the rust projects and I'm gonna go cargo init. This'll just create it for you, it also git inits for you. Beautiful, wonderful, fantastic. And let's open up your favorite editor, which should be them, but we'll find out what happens.
[00:00:31]
All right, so there we go. We have our main function, awesome. So let's do the exact same thing that we've been doing. I'm gonna open up my previous project, I'm gonna yank that line and I'm gonna go back here. There we go, awesome. And so now let's start the conversion from typescript into rust.
[00:00:51]
So rust is just slightly different, right? They have an fn and instead of function, okay, that's pretty straight forward instead of colon, well, dash, arrow, why? I don't know. Now the return types are a little bit more complicated. For this specific one, we have a string in the code.
[00:01:09]
What do you think happens when you compile a program and you have a string in the code? If you had to do this yourself, what would you do with that string? Well, you would probably have to store that literal string in the binary, correct, because it has to be there.
[00:01:23]
So there is a special little thing called static. I know, this is just so great. Really, what I'm saying is, hey, I'm gonna pass back to you a reference to something that lives forever. So this is a static string reference. This is something that lives forever, and that's what it is.
[00:01:39]
So this is our only time we're gonna be touching a lifetime, but it's the easiest lifetime of them all, the static lifetime, it lives forever. And then I'm just gonna simply convert it into that, and there we go. We are now referring to the string that's been compiled into your binary and we're gonna return it to you.
[00:01:58]
And so keeping up with kind of our procedural way, we're going to do the exact same thing. We are going to create a parse line function that takes a line. It's going to split it, and then it's going to return out its value. And we can kinda handle it however we want to, but lastly, you will notice something right away.
[00:02:16]
Hold on, I'm gonna go like this. I wanna keep on going with this. Again, you'll notice that I have an underline here, why? It wants that delicious snake case, the greatest casing of all time, why? Just does, all right, and so let's have the exact same thing that we did in the previous one.
[00:02:32]
We'll have a point again, even though it's kind of a terrible name. And we'll have an x: i32, y: i32, all right? So in rust land i32, 32 bit number and it's signed, meaning that it can be both negative and positive. A u means unsigned, it can only be positive.
[00:02:49]
Just in case you don't know those things, there we go. We are gonna return a point and we're gonna take in a line, correct. That is also gonna be part of that delicious static lifetime, right? Because we're already specifying it right here, we're going to take in the exact same thing right here.
[00:03:08]
We technically, I believe we don't actually have to. We can also just say, hey, it's a reference string, I don't know how long it lives but it lives as long as my function, right? So we can also say that too, so there we go. So now let's start breaking apart this line.
[00:03:22]
So there are some pretty great tools in rust. One of them is split_once. What this effectively does is it's gonna return an option of string string. So if this character exists that I'm going to split upon, it will return it to me, if it doesn't, it's not going to return me anything.
[00:03:40]
So this is actually kind of a cool concept. So let's go like this, let's put the delimiter space. And let's go like this, let a, right, just for now, whatever. What is it? It's an option string string. So what can we do with this? Well, we can actually do some pretty neat things.
[00:03:56]
So one thing we could do right here is instead of using a hard value point to return, we could use an option to return. That would be nice because that means I can do a .map on this. If there is a value there, then I can mutate that value into what I want.
[00:04:11]
If there is no value there, then I will simply have to say, hey, sorry it doesn't work out. But for our sake, we're just gonna kinda keep it a little bit different cuz we're gonna run into some more problems here shortly. So we'll just unwrap or we'll expect, right, cuz expect tells us, hey, if this thing doesn't work out, we have to halt the program, right?
[00:04:32]
And that would make sense. We're doing test input. Let's just do that here and let's go like this, expect must contain a whitespace, right, makes total sense, we would expect that. Now here's the best part about doing that, remember that whole sweet pattern matching that we have available in rust?
[00:04:50]
Well, it extends here, so I'd like this dir and the mount, it looks wonderful, you can see amount is a string, dir is a string. It has tuples in this language which a tuple is simply something that looks like this, right, I can go String, String, that means there's two values and they're both capital S strings.
[00:05:07]
Obviously a reference to a string just points to a string, it's just a smaller type, and it allows you to create these things without copying. So that just means, I didn't copy the string, I'm just simply referring to the string. All right, so we have these but we do have one more problem, which is now we have to convert the amount from a string into an integer.
[00:05:25]
Now remember rust is Mr Safety, right, so that means when we do this what do you think's gonna happen? Come on, it's gonna throw an error, right? It's gonna be like, there's an error here, so we should get a result out of this. So what I'm gonna do is I'm gonna say my type, I expect an i32.
[00:05:40]
And I'm gonna go like this, string parse and now I need to hand it something, amount. So it can tell what it needs to parse to because I have an identified type right here. There's other ways you can do that, I believe parse also takes what they refer to as the turbo fish operator, which will do the exact same thing.
[00:05:58]
And so now you can see I am a result of i32 or a ParseIntError. That makes sense, right, cuz we don't know what it's going to be quite yet. Of course, again, since we are making a little test program, let's use expect, and just say, second arg, my goodness, must be an integer, right?
[00:06:20]
That just totally makes sense. There we go, it's probably the actually the nicest way to do this program, cuz you do want these hard errors if you're expecting something like that. All right, there we go. So now we have, did I forget a semicolon? There we go. Rust requires semicolons.
[00:06:36]
All right, so now we have the direction, we have the amount, we just simply need to return the points. I will say one cool thing about rust, s that if you do have x = to 0 and y = 1, and these things are backwards, you can return a point with x and y and it correctly assigns them to what they are expected to be.
[00:06:57]
So it behaves what I consider more normally. Let's get rid of that, exact same code as before. In fact, it's such similar code, I can technically jump back here and I could just copy all of this and paste it in and it's gonna, oopsies, I didn't actually cut and paste.
[00:07:12]
It will do almost identically the same thing here. Hold on, let me go. There we go, look at that, just so you can see how similar these two languages are. Just simply make it obviously from one to two. It also does not like parentheses around things, and then we just have to change our amount.
[00:07:32]
So I'm just gonna go Point, and go x = amount, y = 0, right? Beautiful, right, that was actually pretty straightforward. We can do the exact same thing here, call this thing y, make it a negative sign. For some odd reason erased it, X0, do that, beautiful, reduce that down, take away the parentheses.
[00:07:55]
And of course take that paste it in, go here, erase that, there we go. It's like the exact same code, right? This looks the same, feels the same. We're not dealing with errors, we're hard halting our program. If we're wrong, everything else should be the same. So now we can do the exact same thing down here, we can go like this, get input.
[00:08:17]
We have our nice input right now. From there, we can do a lines. Lines will simply do that whole splitting on the new line for you. It's actually a really convenient function. I love the fact that they just have this on here, cuz we all split new line all the time.
[00:08:34]
Why not just give us a function to do it. And it feels like it makes it pretty readable. Another thing is rust really prefers you to put the period on the new line like this. If you were to break it down, it's just what they like. So now I can do something like this, which I think is pretty cool.
[00:08:48]
I can now take a map and do a nice x and just simply do this line right here, parse line. We can do this even better soon, but we're not gonna do it better right now. And just take a parse line and do an x. We can even more so just take parsed line, pass it in.
[00:09:06]
Fantastic, this is looking great, right? We're just saying hey, parse this line. And so now our next item should be a point. And so just like with JavaScript, they have reduce, rust has fold, fold is gonna be the exact same thing. So let's use that same method, I'm gonna have to first start off with my initial value of, of course, 00.
[00:09:28]
And from there, I need a function which is gonna be a mutable, cuz remember we're gonna mutate this thing coming in, our mutable accumulator, and then of course our point. Fantastic, did I make this not, I was just hoping you could do that. Sometimes I do things out there and I just really hope that it's gonna work out and let's make sure we always return acc.
[00:09:51]
This is fantastic, and what are you trying to say, I expected this but found that, okay, calm down. The reason why it's saying that, is that, what is the return type of main? Nothing, right, it's the empty object. So something about rust, and people love this, I hate this, and you're gonna see me not use it at all is implicit returns, everything is an expression.
[00:10:12]
If you do an if statement and that's the last line of your function, whatever it returns or whatever is the final value in that if statement is the thing that's returned. So, if you use a semicolon at the end, the semicolon delimits the end of the expression and then an empty thing is returned afterwards.
[00:10:30]
So, me personally, so if we just do this, you'll notice that gets rid of the air and you're like, how did that get rid of the air? It's because this thing was expecting me to return nothing from this function. And so now, I'm preventing it from returning by adding the semicolon at the end.
[00:10:42]
I don't like to have to look forward to know if this is the return statement, so I just don't use those. That's my argument against it. Hate me for it. I bet Twitch chat is just losing it right now. All right, so let's just do this, acc.x+=, my goodness, that's like the third time I've done that, += point.x, there we go, exact same thing.
[00:11:04]
We've seen this how many times now? X/y, why are you not auto completing? I figured it'd be auto completing by now. All right, there we go. So we have this beautiful thing right here. And now we have that, so I can go like this, let result equals this.
[00:11:20]
And I can do that beautiful thing. And if I hit Edit, it's not running, that's too bad, rust analyzer will often put in ghost texts into your editor to tell you what type is on each line if you set up your code like this. For whatever reason, it's not running, which is just sad.
[00:11:35]
But yeah, so that way you would see that this thing was a reference to a string, this was a iterator of reference to string, this was an iterator of point, this returns a single point, right? So you can watch it do its translation step by step, which makes it super easy to look through these things.
[00:11:53]
Now, hopefully, does this all make sense? This looks just like JavaScript, right? But let me tell you a reason why it's better than what JavaScript does, when you use .map on an array, what actually happens? Anyone, just throw out an answer.
>> Iterates through and returns each element.
[00:12:13]
>> Yes, exactly, it iterates through and returns each element into a new array. Filter, what does filter do? It goes through each elements, does a test, and returns a new array with those items. This is not creating new arrays, this is creating a new iterator, that simply when the value comes in, it performs the next step of the operation and hands it off to the next place.
[00:12:37]
So that way no new temporary allocations are created. So if you do .map, .filter, .reduce, you've just created three temporary arrays in JavaScript. You're creating nothing here. So it is inherently gonna be more performant just by simple standards of it just does less. Now, V8's super clever, they might do something, I don't know.
[00:12:56]
But never discount V8, those are the smartest people in the universe. They made JavaScript into a fast language. And so, they could be doing something smart. So if I erase this just for fun, you'll notice what I'll get here is they'll say, hey, let's see. I'm gonna paste this back in for a second and just comment this thing out, and go 9 up, and there you go.
[00:13:22]
You'll notice that it says, hey, this thing's not used, you're not using it, cuz we never did anything with the iterator to consumes its. So a fold consumes the value, cuz it takes it from a something that's waiting to be pulled on to the point of turning it into a singular value.
[00:13:35]
So .ForEach goes through each item, that's a consuming item. You can also call .collect to turn your iterator into say a vector. And so that's kind of like how, it's a small little difference with rust but I think that that thing's fantastic. It's just one of my favorite parts of all time, right?
[00:13:52]
So let result = this, fantastic, remove that. Now we have this, if we look at result, let's just jump up here, look at it, it is Point, so awesome. We have everything we need. And now, here comes one of the best worst parts about rust, all at the exact same time, let's print out the result.
[00:14:12]
All right, it says, hey, Point does not implement the display trait, therefore we cannot print it, you're like, okay, but I'm smart. I am gonna use the special print, now this one is gonna, say, well, it doesn't implement the debug trait, so you can't do that. But if you go up to your struct, you can do, remember that macro business I was talking about, I'm gonna say, hey, you know what?
[00:14:35]
This struct is filled with a bunch of very printable items. I want you to just go derive how to print this for me. I don't wanna have to figure it out, you just figure it out for me. So it's gonna go through and it's gonna do this. Now, this is one of the most powerful concepts out there, right now it just kind of looks silly.
[00:14:51]
But when we do that, you'll notice, boom, we can now print this thing out. And so of course, I don't know what to call this operator, other than a guy smoking a pipe. I don't know what that thing, I'm sure they have some sandwich, the burger, I think they call it a burger, I don't know.
[00:15:03]
I like the guy smoking a pipe, I think that's more funny. All right, so we're gonna do a little cargo run. Of course, let's see, how do we do this, where do we create this thing at? Since this is just our main file inside of our source, we can just call cargo run.
[00:15:17]
So there is a way to have separate binaries, but right now we're just doing this. So, let's go cargo run, it should compile. And what you'll see is that, point has y, 10 and x has 15. Now, remember I flipped x and y, that's why they're backwards right here.
[00:15:32]
The bug procedurally goes through your structure and actually prints it out in order of fields. Pretty cool, right? You get that for free. So, you get the speed of C++ without me having to write that for loop and all that fun.
Learn Straight from the Experts Who Shape the Modern Web
- In-depth Courses
- Industry Leading Experts
- Learning Paths
- Live Interactive Workshops