Check out a free preview of the full Rust for TypeScript Developers course
The "Creating an Iterator" 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 creates a custom iterator for the rectangle struct. An associated type is defined to tell Rust what to expect out of the iterator. The resulting items contain a vector of points along with the index.
Transcript from the "Creating an Iterator" Lesson
[00:00:00]
>> All right, so we're gonna take it to the next level. We're gonna make some things that are a bit useless at this point, okay? But they show us how to use Rust. But even more, they show us how to do the greatest thing in the universe, or one of my favorite things.
[00:00:12]
It's like one step below metaprogramming. It's pretty exciting. So we're gonna start by creating an iterator ourselves. So remember, iterator is not just something we interact with, it's something we can create ourselves. We're going to implement an iterator. Now if you forgot, let's jump back over here. If you remember, an iterator is not the object.
[00:00:32]
So if we wanted to make an iterator for the rectangle, our rectangle is not an iterator. We'd have to create something like a rect itor that would become the iterator that could iterate over our rectangle. So you could imagine, we could create something like this, that takes in a rectangle and gives us each one of the points.
[00:00:51]
So that means we're gonna need some sort of iterator, let's just call it rectiter, To make this easy, by the way, I'm not gonna have any lifetimes. So if we were to make this nicer, we would just refer to our rectangle. But instead, we're just gonna have a list of points that we're gonna walk through, okay?
[00:01:10]
I didn't wanna get into the whole lifetime deal. So, we're gonna do this. We'll have two properties on it, one will be points, which will be a vector of float 64 and the other one will be index. And every time next is called, I'm gonna get the current point that we have on our list.
[00:01:33]
I'm going to increment our index, and then I'm gonna return the current point. So that way, it laserly works through our rectangle. So, let's just do that now. This is kind off exciting, I hope you guys are feeling excited. I'm feeling excited. So, let's go to [LAUGH] our rectangle file, and let's create a new structure.
[00:01:54]
So I'm gonna go to the bottom, and I'm gonna create it down here. So I'm gonna go like this, struct RectIter, all right. And inside of our struct, remember, we're gonna want that points item, so I'm gonna go points, which is gonna be a vector of a tuple that's F64, F64.
[00:02:12]
So a little tuple never hurt anybody. And so let's do an index at 0. And so, there we go. Oop, not 0, sorry, let's go usize, there we go. That makes sense, right? So we're gonna have a list of points, we're gonna walk through them. Again, we could have taken a reference to the rectangle.
[00:02:32]
We'd start doing lifetimes. I'd explain these lifetimes. You'd look at me confused. And then I think it just wouldn't be a w. So, we're not gonna do it that way. Plus we're gonna delete this here shortly. So, we have our rectiter. But how do we make it iterate?
[00:02:47]
Well, it's always a trait, right? Impl Iterator for RectIter. Now, you're gonna see something that's a little bit different here. This is called an associated type. This is us telling Rust, this is what's gonna come out of our method every single time. So they're called an associated type.
[00:03:12]
So I'm gonna say, okay, my associated type is gonna be float64, or a tuple of float64s, right? That's this thing right here. So, now that you have the associated type, we need to implement the next method. And the next method, remember what we have to do. Get our current point, Increment our index.
[00:03:38]
All right, here we go. Are you ready for this beautiful thing? So now, we need to do this. So first, our point is going to be an item inside of, well, inside of our list. So we can go like this, self., let's see, points. Now, here's where it gets kind of interesting.
[00:03:58]
So let's kinda break one of our rules, or maybe we can do it really nice. Let's find out which one we can do. So I can do get at self.index. But let's look at that for a second. What do we get out of here? We get a reference to the point.
[00:04:11]
Cuz remember, when you do a get, you don't get the item, you get a reference to it. Now I think we could use this method called drain to just pull out the items one at a time out of our vector. But we're not gonna do that, instead we're gonna do it a little bit differently.
[00:04:24]
We'll go like this. If self.length, well, my computer is chugging along all of a sudden. If, not self.length, if points.length this is less than, oopsies, what am I doing here? If self.index < points.length, we can return the correct thing or to invert it. This is just really stupid logic right here, so just deal with it.
[00:04:52]
There's probably some nicer methods here. If it's greater than that, we can just return None, right? There's nothing to happen here. This is just what it is. We're done with our iterator. So once you return None, your iterator stops iterating, that is the signal to the outside world you're done.
[00:05:09]
I believe with Python, you raise an exception to say that you're done iterating. It's a stop iter, I forget what it is. Anyways, look at this, we can now just use our nice little bracket notation if we want to. And look at what we get out here, we get this beautiful little item.
[00:05:26]
Look how nice that is, all right? And now we take ourselves index += 1, and we can return out point or technically some point. Cuz we have to return out an option of the thing, cuz remember, it could be None cuz None is the end. So I'm trying to write this as simple as possible.
[00:05:47]
And just like the most, I guess, you could say in the most TypeScripty way. We could have mapped it, made it wonderful. All right, that was fun.
>> Why is index mutable, I mean, it feels like for some reason, I'm tripping myself up that self.idx += 1, there we go.
[00:06:12]
>> Okay, a new cases. So the reason why it's mutable is that the only way to iterate through something is that the underlying iterator has to be mutable. Cuz you have to be able to change its state until you're at the end. And once you're at the end, you call it.
[00:06:25]
So I think we could do this a little bit better. Let me give this a quick little try here. I don't know why my brain's blanking so much, but we could do this. We could go let idx = self.idx. We could do self.idx += 1, and then we could return self.points.get(index), and then maybe toss on a little bit of a map here.
[00:06:54]
And because remember, look at what we're having right here. We're having a, what's the type here? Does anyone know what the type is? It's a reference to the thing. So since we should be able to just do that, if I'm not mistaken, so what this will do is this will dereference it.
[00:07:08]
All numbers implement copy, meaning that when you take, you can just automatically copy out something. And so this will automatically copy it for you. When I dereference it instead of becoming the owner of the item, it copies it out. So kinda cool, little start, it doesn't work with things that haven't implemented the copy trait.
[00:07:32]
So we can even make our own version of copy to where, when you pass it into a function, you can pass it in as the value and underneath the hood, it'll copy it instead of moving it. So this is instead of moving, it's copying. Does that make sense?
[00:07:46]
Sort of It's a weird semantic, right? And well, what are you doing here? All right, fantastic. We've now implemented this delicious iterator. We should be able to now do some iterating. But how do we do that? We've created the iterator. We've made it do the iterating. But how do we get it?
[00:08:07]
Cuz I don't want them to have to like, hand initialize it, right, I'd be kind of annoying. So, let's go over here. I'm gonna make this thing pub. I believe you have to make it public because we're gonna now leak it out to the world. I'm gonna implement into iterator, for Rectangle, wait, Rect, that's what we called it.
[00:08:30]
So now, we're gonna be able to convert our rectangle into an iterator with this trait. So, let's go like this. Let's implement the missing members. Now look at this, now we have two associated types. I know, it's kind of getting kind of crazy here, right? So our item that we're gonna be returning, what's the item that we're returning from our iterator, does anyone remember?
[00:08:52]
>> Our points?
>> Yeah, a point, a tuple, tuple of float 64. So float 64, float 64, and then our iterator. What is our iterator?
>> Rectiter.
>> Rectiter, there we go, nice, Rectiter. So now we just have to implement the thing right here. So, let's return a Rectiter.
[00:09:19]
And now, all we need is we need the points, well, that should be pretty easy. I can just go a vector and do self.x self.y, and then do self.x + width a copy that a couple times, and then do self.y + self.height, and then do that. There we go, look at that.
[00:09:48]
So that right there would be the four points, right, xx, x + width, x + height, and then x + width, y + height, right? Have all the four points. And then our index, we're gonna want it to be 0. And then, of course, I forgot the word self right there.
[00:10:08]
And why are you, of course, I forgot to make them tuples. So let's go pop up up, kinda make them tuples. No, all of a sudden my foo is falling out of place here. There we go, perfect. So, made them tuples, I forgot to wrap them in those nice little parentheses.
[00:10:39]
Classic parentheses problems.
Learn Straight from the Experts Who Shape the Modern Web
- In-depth Courses
- Industry Leading Experts
- Learning Paths
- Live Interactive Workshops