Rust for TypeScript Developers IntoIterator & From Traits
Transcript from the "IntoIterator & From Traits" Lesson
>> All right, I wanna move on. Is that okay with everybody? If you haven't finished, just go to the course website and you should be able to get the complete code right here. Look at that, it's right there. And look at this, I did this the kinda dumb way right here.
[00:00:13] How cute is that? Okay, so there we go, fantastic. We now have it. So when you do IntoIterator, it consumes the thing you give it. I know that's a weird word, but remember when you passed your item to a function, the function became the owner. So let's go back to here and let's take our rect right now.
[00:00:41] I'd like this now, I can go, for point in rect. Look at that, we can now for loop over our rectangle because we created this into iter. It's able to understand our into iter and iterate over it, but what happens? If I go like this, println and pass in my rect, what's the error we're getting?
[00:01:04] We've seen this error before, haven't we? Borrow of moved value, who owns the rectangle?
>> The iterator?
>> Yeah, the for loop. The for loop was handed the rectangle. So now you don't own the rectangle. You can really see this very obviously doing it with a vector. I'll do it really quickly.
[00:01:26] If I go let vec = vec![1, 2, 3], I can create the exact same thing here. Do that, I can print out the vec. Do one of those and I can go for value in vec, and now we'll have the same problem. But if I go like this, we no longer have the problem, why?
[00:01:52] What have I told you about these references? It's not just a reference. A reference in of itself is almost like a type, right, sort of. Well, let's jump back to our rectangle. Let's just take this entire block. I'm gonna copy the whole thing. I'm gonna paste it and I'm gonna implement iterator for a reference to rectangle, not the rectangle itself, but a reference to rectangle.
[00:02:22] So let's just see if that works because if this is a type, then we can kind of implement it for that version of the type. So I'm gonna go back to main. So for those that don't see, I just put a little ampersand right here. So we implemented IntoIterator for a reference to a rectangle, which means I can delete this.
[00:02:41] And right now we have this problem. Now when I do that, problem's gone because the reference to a rectangle gets the method IntoIterator called on it, which means we're consuming a reference, we're not consuming the rectangle. Does that make sense? I wonder if this is, don't worry about it.
[00:03:07] All right, so there we go, pretty fantastic? So if you didn't hear the question, can we do that without copying and pasting? Well, we can take it in multiple steps. So right now, we're almost achieving maximal wrong. Like I said, we're gonna first fail to succeed. So I'm just gonna keep on going down this path in the worst possible way, cuz what are we gonna have to do with circle?
[00:03:28] We have to do circle iter. And we're gonna have to have the iterator for that one. Then we're gonna have to have the iterator for the reference to circle. It's gonna be a lot of things, right? All right, so let's go back here. Simple fix, right? Just copy and paste it.
[00:03:42] I hate duplicating code. I know the Martin Fowler rule of thumb is three duplications mean refactor. But for me, with this type of blatant copy and pasting, I do a two duplication let's refactor. So the first thing we do is do it via a constructor. So what I mean by that is a pretty typical way to do this, you don't have to copy me on this one, is that I can do something like this.
[00:04:07] Implement RectIter, I can add a function to RectIter called, let's just say pub new, or it doesn't need to be pub. It can just be function new. So it's only within our file. Only we can construct an iterator. And this thing could say, take a reference to rectangle.
[00:04:27] If it has a reference to rectangle, it's gonna return out an iter. Now, you can call this function whatever you want. There's not a rule that new means it has to be the RectIter. And also if you want, you can also use self if you'd like to. So there we go.
[00:04:44] And of course, oopsies, don't call it a rectangle, it's a rect. So there we go. So our problem is we need to just build this. So I can take this code right here [SOUND] and I could jump up here, and I could paste it in. So now look, we have this, but we have one problem.
[00:05:02] Obviously, self doesn't exist anymore. We need to refer to the rectangle. So I'll just highlight these, do a little tiny, teeny, tiny little regex replace here. And there we go. We now have rect being used, but not consuming it, only consuming a reference to it. And there we go, fantastic, right, right?
[00:05:24] Okay, so that means we should be able to go down here. And we should be able to say this, okay, return RectIter new reference to self, right? Because this one right here is a consuming one. Right now, we're actually consuming rect. So this will still cause it to consume.
[00:05:44] This one is a reference to rect, so we should be able to just go like that, if you highlight over it. So reference to rect, there we go. So we've kinda created less duplication, right? That feels pretty good, right? Yeah, I mean, it's better. I'm feeling happier about that.
[00:05:59] There's still a lot of duplication going on here. One other thing we can do is instead of doing that, we can go like this, implement from rectangle for RectIter. So this is another trait. This is called the from trait. So this will allow you to effectively transfer types or change types.
[00:06:23] So I'm gonna go like this. I'm gonna implement the missing members. It's gonna have a value. And I'm just gonna take out that and just move it up here. And of course, rect becomes value, there we go. And I can delete this. We no longer have the constructor, so I now have this right here.
[00:06:42] It's called a from. And this is a pretty cool trait because it allows you to do something like this, where I can go self.into. And the thing that's really cool about that is that the compiler infers what type should it go into. Just like parse, remember how parse, I had to say a type somewhere, and it could just define what parse method to call?
[00:07:01] This is doing the exact same thing, except for it's doing it with into. It can tell right here that I'm gonna need to be able to be this associated type into iter, which is actually gonna be a RectIter. And so when I call into, it goes, okay, RectIter has to implement the from reference rectangle, and it will just automatically happen.
[00:07:21] So that means I can just go up here. I can delete that and do one of these and that and go into. There we go. So now I'm just using a bit more inference, using a bit more standard way to do this. You can kind of choose whatever way you want to do it.
[00:07:34] I don't think one way is more right than the other. But I think into is super cool to use because it allows you to have more tying into the standard. I feel really good tying into the standard library at all times because now I just have a more uniform way to be able to go from one shape to another.
[00:07:51] It also means that if we are in actual library code, right, I could do iter = self.into, and I don't even know what type this is. I could do RectIter, right? And it would just be able to do the right thing. Hey, what are you doing here? Oopsies, it hasn't implemented on the reference.
[00:08:13] There we go. So it can do that. It can just do the right thing. And then you can also do a consuming into in which it will do, it will actually consume the value. So if you actually wanted to transfer it from a rectangle into a RectIter, you could have it implemented on the actual item itself, pretty cool stuff.