Check out a free preview of the full Rust for TypeScript Developers course
The "Rust Basics" 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 compares common programming constructs in TypeScript to the equivalent implementation in Rust. The examples include variables, functions, loops, classes, and interfaces.
Transcript from the "Rust Basics" Lesson
[00:00:00]
>> So we're just gonna jump right in and start going over the basics. There's no code in this section, but I just wanted to kinda show you some things so that as we start doing it, you can try to recall from your memory. The more you recall from your memory, the better you're going to be, the more it's gonna set deep within your head, and then you can just start doing it yourself.
[00:00:17]
For me, I had to type something out like four or five times before it really sticks. And so hopefully just try to follow along. I've made tons of little exercises so you can type the function out ten times. So by the end, you just can do it without thinking.
[00:00:31]
All right, so again, there'll be no programming. So I just wanted to go over a couple differences in Rust versus TypeScript. So first off variables in TypeScript, here you go, this should all look familiar. const foo, sort of a constant. let foo, this is a variable you can change.
[00:00:45]
const foo, as a const. This is a great C joke. Here's Rust. Rust looks pretty similar. This is a constant. It's always a constant. There's no changing it no matter what its data structure is. When you say let foo, you cannot change foo. let mut foo, you can change.
[00:01:01]
You can change its value, all that. So there's only two types. There's not this third, weird version that partially applies. All right, another thing that's unique about Rust with variables is we have shadowing. So this would be illegal in TypeScript. This is completely legal in Rust. This is really, really handy.
[00:01:20]
As you can see right here, you can get a file, you can read a file, you can tokenize the file. So you can just have like file, file contents. Or you can do something where sometimes you want to use the same name to describe this thing as it changes, and you can't do it in TypeScript because you're changing types.
[00:01:33]
But in Rust, shadowing is perfectly fine, as long as you use the let keyword right beforehand. So foo right here is, say, some sort of vector. foo right here is whatever the method returns. Super cool, very, very handy. At first, I hated it cuz I thought, who would do this?
[00:01:46]
This is bad programming. Why would you ever wanna do this? Why would we be changing types on a name? Now I can't help but to use it all the time. I don't know what happens. Life changes how you use it. If conditions, looks pretty straightforward, right? They're the same thing.
[00:02:00]
Drop the parentheses. If you add the parentheses, Rust is just gonna warn you, say, hey, you can't do that. Or it's gonna say you can do that, but you shouldn't do that stylistically. You don't need to do that, then we're done. So pretty straightforward. Loops, almost the same.
[00:02:14]
So here's a basic for loop. In Rust you use a range. So for i in 0 up to but not including 10, or for i starting at 0 up to and including 10. So you got inclusive exclusive ranges, you can also assign ranges to variables, you can use them.
[00:02:33]
They're actually just like a fundamental concept. Pretty fun. While loops, again, pretty much the same between the two languages. You already know them. Just don't use the parentheses. You do, you'll get warned. Forever loops, you either have for colon colon or semi colon semi colon that. Or while true, in Rust you just have loop.
[00:02:51]
So pretty akin to something like Go that had for. Collections, when you're iterating over a collection, here's your classic for of loop for any object right here for entries. You have it for an array or you can go over indices. These all look familiar. I assume everyone here is familiar enough with JavaScript to feel okay about that.
[00:03:13]
You also have these things that exist. People kind of associate them with this whole looping thing. You have array.map, which does clone your array. You have array.filter, which does clone your array. You have array.forEach, which iterates over your collection. array#reduce, always a bad decision. And map.forEach, the weirdest interface in the universe.
[00:03:30]
Value slash key is just the strangest way to do it. But that's how arrays do it. I know, it's a map, though. It's a different data structure. Why do you got to do that, TC 39? You're killing me. All right, Rust, it's a little bit different. You just go for x in some array or in something, you don't have to have this little ampersand here.
[00:03:49]
We will talk about that later. And you can do things like this. That's just a vector akin to a list, and I can map over something, or filter over something, or do anything. And then at the end, I can just tell it what data structure to become at the end.
[00:04:03]
And so in this case, I'm telling it to become a vector, or a list, of the same type that's up here. So probably i32, I think, is the default one. I don't remember what it is. Functions, again, pretty much the same, as you can see right here, function, fn.
[00:04:18]
We just call them functions. Parameters, again, pretty much the same thing. Arg colon type, arg colon type. Everyone that's familiar with TypeScript, you already pretty much know a huge amount of Rust syntax. There's only a few things that get a little bit complicated with the syntax. Returns, this is like, depending on how you view the world, this might be a little bit harder.
[00:04:43]
So a very popular notion, especially among them Twitter elite, TypeScript devs, is don't do return values or don't do return types on your functions. You have to do that in Rust. You got to tell it what you're gonna return. There's no implicit return types. You're just gonna have to hashtag deal with it.
[00:05:01]
And so this is legal in TypeScript. You just couldn't do that in Rust. You have to tell it, I'm gonna return a usize. We'll go over the different types of ints and all that just because, if you've only done TypeScript, the word number has so many meanings, whereas in Rust, it's reduced.
[00:05:17]
All right, closures. These are all pretty straightforward, right? You can define a closure with a parameter and then return it, or you can do kind of more this, like, auto return. If it's a single statement, you don't have to do anything. Rust is identical. The only difference is that we use a bar instead of a parenthesis.
[00:05:33]
It emotionally hurts me that we use a bar. But it does make sense when you know about the unit cuz then you couldn't do an empty closure and it all breaks up. I believe that's the reasoning. I haven't read that. That's just my own personal, self defense mechanism for why we had to use bars.
[00:05:48]
But there you go. Pretty much identical, right? Nothing seems too hard here. Classes and methods, again, TypeScript, you do class, properties, constructor, methods, static, private, protected. You kinda have all these different variations in which you can work on classes. I know some of you right now, you see the word class.
[00:06:08]
Man, you're like getting hot and bothered because we're functional programmers only in TypeScript land. I get it. I'm sorry. This is traumatic for some of you. Now Rust does it a little bit different. I want you to really look at this just for a moment, is that you define a struct which is a series of properties or pub properties for things people can access on the outside, so this will be private.
[00:06:30]
This is public, there's no protected, there's no package level scoping, it's just pub private, and then you have an implementation. And so then I say, hey, the implementation for foo, which that was the name of my struct, here is a function. This would be a static function. This would be a public static function that anyone can use.
[00:06:49]
This would be a function that refers to itself or an instance method or a method as we'd call it. This would be a method that could change values on itself. You kinda have to define each type of function cuz each one has different implications, and that will become evident when we talk about the Baro checker.
[00:07:05]
You can also make them public, so this is a method in which takes not a reference to yourself but the self itself. That may be a little bit confusing, what the heck's the difference between a reference to yourself and a self? We'll get there, but each one's a little different.
[00:07:20]
So what stuck out to you, the difference between class definition and how Rust does it? Just one thing.
>> The data and the behavior are defined in separate blocks, very explicitly separate blocks?
>> Yes, so if you couldn't hear that, data and behavior are defined in two blocks.
[00:07:39]
This may seem kind of like a trivial point, but trust me, this is like the coolest thing in the universe. This will blow your mind by the end of the day. This is like the greatest thing ever. This allows for composing behavior, not upfront definition of behavior, because that's one thing that classes are really frustrating about.
[00:07:57]
And that's why you have abstract classes. This is why you have hierarchies, because you can't wrap on additional behavior in any easy way. You have to IOC it, you have to do something else, you have to create a new class, you have to compose it in a more difficult way than you can, say, with Rust.
[00:08:11]
So very exciting, trust me. Oop, we got a question.
>> So struct is like interface, and impl is like a class that implements that interface?
>> No, so struct is not an interface. Struct is a concrete set of properties. And one thing you have to break, and we'll kind of talk about this here shortly, is that memory plays a huge role into this.
[00:08:34]
When you're in JavaScript, you just create things. You don't think about how they're laid out in memory. You don't think about what it's actually doing. In Rust, you have to define the blueprint of exactly how this thing is gonna be laid out. And then Rust lays it out in memory.
[00:08:47]
So this actually has a defined size to it. There is a certain amount of bytes that are associated with it. And so, this is the concrete item, you can instantiate a foo. This is just the implementation, it creates functions that will hang off your struct, so that way you can go struct dot whatever, or foo dot this.
[00:09:08]
And you can call an instance method, or foo colon colon to call a static method. All right, interfaces. Interfaces are really where the super secret sauce of Rust takes off. Not so great in TypeScript. You have two different things in TypeScript. You have type and you have interfaces.
[00:09:25]
Interfaces, particularly, I'm not very fond of in TypeScript for two reasons. One, you can have properties on interface. I just think that's not a good move. They like that move. And second, you can do this whole interface merging thing. So if I define foo here, and then I redefine foo here.
[00:09:40]
Foo is the amalgamation of both interfaces. So you'll actually have properties, method, and, hey, another method required to be a foo, which both has some cool things you can do with it. Meaning that if you're trying to integrate with a library but extended to have your own behavior, you can add methods to interfaces.
[00:09:57]
But at the same time, you run into these bugs where you're like, why is this like this? I'm looking at the definition. Why do I need this extra method on here? It's because someone else has defined it and you don't realize, you've just been interface merged. It's unique, it's cool, but it is what it is.
[00:10:13]
Whereas Rust it's a little different. Trait is effectively an interface, and you can define the same things, but no properties are allowed. And how you implement a trait is you do the same thing. The implementation of foo for, or the implementation of trait foo for my struct, struct would be just like a struct, it can also be an enum.
[00:10:35]
So that's the syntax for it. And then you put in the methods that have been defined here, and only the methods that have been defined there. Yeah, Mark.
>> Could you elaborate on why defining properties on an interface is bad and why you want to avoid it?
>> In general, I always feel like prop, this is just a personal belief.
[00:10:53]
Okay, we're getting into the personal realm, I will accept that as being true, is that properties are an implementation detail. They are something that I'm gonna manipulate. They're my state bag. This is how I interact with my system, my closed system. And so, I've just never been fond properties on interfaces, a lot of languages never allow you to do that, Java, plenty of other ones, Rust, whatever.
[00:11:15]
And so, it's a unique take to allow properties on it, I'm not sure why they made that decision. I bet you there's a great reason for it. It just feels funny to me to have properties on an interface, because interfaces aren't supposed to have any implementation requirements other than what methods you're supposed to have.
[00:11:33]
This one's both having your data shape and what methods you should have, which is kind of unique in some sense. Anyway, because who cares how you define length. If it's a function, you can define it however you want. It could be amalgamation of three different properties. But by enforcing a property specifically, now you have a different way you have to tackle things.
[00:11:55]
It is making an implementation detail for you. There you go. All right, one more question.
>> Do you still need a struct?
>> For a trait? If you're asking do you still need a struct, yes. Structs and traits are two unrelated things in some sense. A struct is your property layout, exactly what's gonna be on the item.
[00:12:15]
A trait is an implementation of a method on that struct. It's how you can interact with the struct. It interface onto it. Yeah.
>> Can you use a struct without having an implementation?
>> Can you use a struct without having an implementation? Absolutely, it's just a series of properties.
[00:12:33]
If you've ever used C, it's effectively the same thing as a C struct. It may be packed in binary, slightly different. I believe you can even make it packed in binary the same way, but it has its own packing and it does what it does. It's just a series of properties.
[00:12:49]
That's all it is. So you define your data shape, then you define how that data shape interacts with the world. So, it's different, it allows for composing.
Learn Straight from the Experts Who Shape the Modern Web
- In-depth Courses
- Industry Leading Experts
- Learning Paths
- Live Interactive Workshops