Check out a free preview of the full Rust for TypeScript Developers course
The "Creating Modules" Lesson is part of the full, Rust for TypeScript Developers course featured in this preview video. Here's what you'd learn in this lesson:
ThePrimeagen demonstrates how traits can behave like a prototypical inheritance because they can add a method to types not owned by the program. The code is then refactored, and a Rust module is created by importing the necessary traits.
Transcript from the "Creating Modules" Lesson
[00:00:00]
>> Here's the complete code, right? Our shapes has public everything. We've named it all public. And then our main imports everything, and I noticed that I don't have the mod statement right here. There should be a mod statement right there. And we have our stuff being created. All right, so now do you see why it's better yet?
[00:00:17]
Can someone name something that's weird going on? Where is rectangle and circle defined at?
>> In a separate folder.
>> Yeah, a different location. Yeah, we just implemented a trait we defined for an object we did not define.
>> So the data structure can be done independent of the data behavior.
[00:00:42]
>> Boom, that's a great answer right there. Your data structure can be separate from your data behavior. This is very, very important. In fact, it's so dang important, I can jump down here and go like this, implement Area for float 64. I'm just gonna take one of their own built-in types and I'm gonna return self times self.
[00:01:02]
And boom, right down here, we go in here and go like this, 6.9.area. Look at that, I just added area to something I don't even own. Okay, we're getting a little bit more excited, right? That's pretty, I mean, for me, that is just so cool. It's so dang cool, but you know what?
[00:01:20]
Y'all a bunch of TypeScript developers, so what are you thinking right now?
>> Monkey typing?
>> You're thinking, whoa, polyfills, global space, something's wrong here, right? Man, last time people messed with my prototype, you had to fist fight at your job, right? No one messes with the prototype.
[00:01:36]
You never do that, right, right? I wanna hear some rights out of you because that is dangerous, okay? You guys are kind of agreeing with me, but like, well, you gotta do what you gotta do.
>> [LAUGH]
>> No, you don't, don't do it, terrified to do that.
[00:01:50]
All right, so let's do a little exercise here, okay? Let's take trait and let's move the Area trait into the shapes.rs. So I'm gonna go over here. I'm gonna take the Area trait plus its rectangle definition, plus its circle definition, plus its float definition. And let's take that and let's move it all the way over here to shapes.
[00:02:16]
Now, of course, we need to do a little pub on the Area trait because we gotta be able to import it in. So very, very important, and I'm gonna hit Save. Okay, there's some warnings probably popping up. Don't read the warnings. They're gonna ruin the surprise, all right?
[00:02:34]
And then there's, of course, this PI down here. Make sure you have PI properly imported. Don't forget about that. Make sure you've done your searching. Don't look. Don't look, don't, okay, there we go. All right, so let's jump back over here. And what happened? Where do our area go?
[00:03:01]
Here's a little magic that happens. You can only use trait methods if you import the trait itself. There is no global mushing of the float. It only exists within the files that have the trait in view. So it is localized monkey patching, if you will, which means it's safe, it's not crazy.
[00:03:26]
When I define area on a float, only I and others who have that specific trait in view get that method on a float, nobody else does. Which means this little thing down here, where are you? I know you're coming up. You can't do that, right? Cuz in JavaScript, if you were to add this to, say, the number of prototype, you would get the same effect, except for everybody in the entire VA isolate would get the exact same experience, which is not good.
[00:03:56]
That's not Ws. Those are not Ws, everyone, no, no high fiving. No one's happy with your polyfills. And so this makes it pretty cool. In fact, this makes it so dang cool if you haven't seen this YouTube video by Matt Pokok, he does like, I fixed TypeScript. And he adds a bunch of extra safety things, you've seen it?
[00:04:12]
Imagine if he could use this instead of setting it for an entire project. Effectively, he fixes how JSON, if you JSON parse, you get an any out. Instead, you get an unknown type out, which adds extra safety. So you can imagine if you could just only require that behavior in for a singular file, you could actually slowly convert your code base to an even more strict version of TypeScript.
[00:04:33]
As opposed to just turning it on for everyone, now you have 9,000 errors. So that's one super cool thing about traits is that you have to have them in view. One super uncool thing about traits is that you have to have them in view. Sometimes you're like, I just wanna read.
[00:04:48]
I know there's a read function. And then you have to go find the trait and sometimes the Rust analyzer struggles with finding the right thing to import in, and then you feel very frustrated. But it also adds pretty cool behavior. I think we can all agree, pretty dang cool behavior.
[00:05:01]
I think it's pretty cool. Okay, maybe I am making it bigger than it is, but for me, this is super cool behavior. All right, but we haven't hit the best yet, all right? So this next part is gonna be a lot of typing, okay? Because we must fail to succeed.
[00:05:19]
We're gonna do something that's gonna get complicated and wrong and all these terrible things, and then it's gonna become amazing, okay? So we're gonna organize our files a bit more, all right? I mean, really, we can just drop this whole TypeScript thing altogether. I don't know why I'm keeping it going, cuz now we're just into this fantastical trait stuff.
[00:05:41]
But you can, you can organize your TypeScript files or you can just say, forget about it, whatever you wanna do. I'll organize them, you can watch. There's only one more tiny bit of TypeScript. The rest is gonna be all Rust from here on out. All right, so we're gonna take from shapes.rs, and instead, we're gonna have this right here, a shapes/mod.rs.
[00:06:00]
So when Rust is trying to resolve one of your mods, one of your includes, what it does is this right here first checks for shapes.rs. If it can't find that, it checks for a folder named shapes with a file inside of it called mod.rs or module. So think of it like index.ts, same effective principle.
[00:06:23]
So I'll just do that right now. I'm gonna create something called shapes. Inside of shapes, I'm gonna create mod.rs. I'm gonna save that. See, notice it says files not included module tree cuz there's already a mod.rs. I'm gonna go in here, I'm gonna go back. I'm gonna go to shapes, I'm gonna take all of shapes, I'm gonna delete shapes.
[00:06:42]
I'm gonna go over here and I'm gonna paste in shapes. And this little warning will stick around for a little bit as Rust analyzer updates. And so now we have shapes/mod.rs as opposed to shapes.rs. So we just moved it because now we're gonna kinda build out a module.
[00:07:00]
We're also gonna perform some of the worst math known to the universe, because it's irrelevant how good our math is, but it shows something pretty cool, all right, shows something pretty cool. So we're gonna scroll back down, and now I want you to break it up even further.
[00:07:13]
I want to have a rect where we put the rectangle. I wanna have a circle where we put the circle. And then I want area where we just put the area trait. So I'll show you what we do here, so let's go. I'm gonna create a couple of files, area.rs.
[00:07:27]
I'm gonna create, what's the other one, rect.rs. I'm gonna create circle.rs. Now I'm gonna go back to my mod file and let's grab out just the area trait definition, there it is. I'm gonna take that, delete it, go over to area, put it right there, that's it. I'm gonna go back to our mod file, and I'm gonna grab out the rectangle, plus I'm gonna grab out the rectangle area definition.
[00:07:57]
Take that out, boom. Then we're gonna go over to rectangle, I'm gonna paste it. Do the same thing with circle, go here, grab the circle definition, and its area definition, and I'm gonna move that over to circle. I'm gonna go back to mod. I forgot about this little float64 business going on here.
[00:08:18]
So let's move that to the Area trait one, so get out of here Area trait one. And now we have the Area trait plus the implementation for float64 inside the area file. So just to recap everything we just got done doing, the area.rs takes the trait and the implementation for float64.
[00:08:39]
Rectangle has the rectangle definition plus the implementation for area. Circle has the circle definition plus the implementation for area. And mod, what do we use in mod? Well, we need to tell Rust what we want to export from our module. So I'm gonna say, hey, public mod circle, public mod rectangle, and public mod area.
[00:09:11]
I'm letting Rust know what I export. And so right now, the analyzer still hasn't caught up. We wanna LspRestart. Sometimes this helps catch everything up. You must delete your shapes.rs. If you don't, it won't be able to kind of include it correctly. So to test it to make sure you've done it correctly, you should get this file to compile correctly, your main file, which notice that shapes no longer has rect, circle, and area anymore.
[00:09:37]
We've kind of created a deeper module. So I'll go like this. I'm just gonna delete that, and I'll just use my importing, the code actions to import it. Notice that it's shapes rect Rect. So if I do the same thing for circle, and I just auto import it, it'll be shapes rect Rect, circle Circle.
[00:09:56]
Because that kinda define these little submodules inside of my shapes module, right? So we're kind of creating a little bit more enterprise application going on right now, feel the enterprise. And then I think, Wait a second. Okay, my cargo is just completely, or my Rust analyzer is completely falling apart.
[00:10:24]
I use Vim, by the way, and so we gotta wait a second. All right, perfect, there we go, which means I should have an error down here. This really should error. Why are you not erroring? So let's cargo build. If you've done this right, you can go cargo run.
[00:10:38]
So I knew this would be the problem, area is not found in the scope, right? That's why we can't call area here, so we need to import the Area trait. There we go, I now have the Area trait. So you should have something that looks like this, use crate shapes rect Rect, circle Circle area Area.
[00:10:55]
I'm just kinda grabbing everything out of that module, all swift-like. So now, if you've done it correctly, you should be able to do this. But look at that, we have errors. Okay, so that's what was going on. So I'm gonna jump over to rectangle, and notice that we haven't satisfied its import conditions, haven't we?
[00:11:13]
So go to your rectangle file and you're gonna have to go and import in area. I just used the auto importer again, do that, bang, you're good to go. So super just means one module up. So I'm gonna go from rectangle to shapes, into area, grab out the Area trait.
[00:11:32]
And if you wanna bet, I bet you if we go to the circle one, you have the same problem. So let's go to circle, and look at that, the same problem. So let's import area, and then we also don't have PI imported. Let's import PI from the 64-bit standard, very important to do.
Learn Straight from the Experts Who Shape the Modern Web
- In-depth Courses
- Industry Leading Experts
- Learning Paths
- Live Interactive Workshops