Check out a free preview of the full Rust for TypeScript Developers course:
The "Refactor Rectangle and Circle" 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 refactors the rectangle and circle shapes to implement the Point and Contains traits. In order for rectangles and circles to be able to detect each other, the Collidable trait is refactored to include two generics.

Get Unlimited Access Now

Transcript from the "Refactor Rectangle and Circle" Lesson

[00:00:00]
>> Because points is a function we have defined that needs to exist. And contains_points or contains_point is a function that we have defined that needs to exist. Long as T implements both of those, We will be able to do this algorithm. Does that make sense? Okay, so let's take that and let's go back to our rectangle and get rid of rect data, it just doesn't need to be there anymore.

[00:00:29] Let's implement points for rectangle, let's implement contains for rectangle. Let's implement points for circle, let's implement contains for circle, all right? So we'll do that now. So let's first go to rectangle. And first off, do we need these collidables anymore? No, cuz we have a generic implementation for them.

[00:00:49] So Collidable Rect for Rect, Collidable Circle for Rect, get them out of here, all right? Now, let's implement points for rectangle. So I can take our rectangle implementation and say, impl Points for Rect. And then make sure we just import it in. Okay, hold on, hold, hold, hold, all right, we don't do those things on there.

[00:01:14] Whoopsies, not Points, that's Contains. My bad on that one, there we go. Do Contains, And so now, we need the impl Points for Rect. There we go. So we have contains, we already had contains, right? That was a part for our implementation on rectangles, so we already had that.

[00:01:33] So now all we have to do is implement points. We technically already have points implemented. If you scroll down yonder, where are you? We already have it right here, there's our vector. Do you see that little vector right here? So I'm gonna grab this little vector. I'm gonna delete that little vector because we don't need anything to do with RectIter anymore.

[00:01:50] I'm gonna walk up two points, and I'm going to just replace that. I'm gonna return that. I'm gonna replace value with self. And of course, I'm gonna call into, because remember, we implemented vector of point float into point iterator, so we should be able to just do this automagically.

[00:02:21] I know that was a lot of things that just went on there, goes a little fantastically fast right now.
>> Should it be runnable by this point?
>> No, we should not be running anything right now.
>> So how do we know if we have problems?
>> Well, first, let's finish doing some cleaning up.

[00:02:39] One thing we can get rid of, do we need area defined for rectangle? Sure, Default? Sure, Display? Sure, do we need RectIter at all? Nope, do we need Iterator for Rect? Nope, do we need From Rect for Iter? Nope, do we need IntoIterator for Rectangle? Nope, do we need IntoIterator for rectangle reference?

[00:03:02] Nope, we don't need any of that. Now, we have a pretty small function, and we've only implemented these two extremely basic behaviors, one to give us a set of points, one to tell you if a point's contained, so pretty basic. Let's do the same thing, but let's do it for circle.

[00:03:24] If you're getting a little stuck, remember you can jump on the website and try to look at the code and try to see what you're doing differently. So let's go to circle, do we need Collide Circle to Circle? No, do we need Collide Rect to Circle? No, do we need area?

[00:03:39] We can keep that. All right, so we have implement Circle that contains point. Well, guess what? Implement Contains for Circle. We don't need pub, of course, because you just don't need pub. There we go, we've already implemented Contains for Circle, awesome. Now, all we need to do is implement Point or Points for Circle.

[00:03:59] So impl Points for Circle, and then, of course, do that. Now, all we have to do is go through here and just do the exact same thing. So I'll just go like this, return vec![(self.x, self.y)].into, because we had that nice little convenient method. Look at that, there we go, we've done it.

[00:04:22] And now, look, also we can get rid of, do you see this rectangle up here? We don't need to have that silly circular reference anymore. We don't need rectangle anymore, whoopsies, and we also don't need Collidable anymore. We only need Contains Point or Contains and Points, and that is it.

[00:04:42] We've done these things, we've implemented them all. And so now, theoretically, we should be able to go back to our main file, and it should just all work. But something's gone wrong here. Why, why is that wrong?
>> Cuz we assume the same type for the comparator.
>> So good at this, my goodness.

[00:05:06] So yeah, we have assumed the same type. Let's jump over to collision. Let's re-look at our blanket implementation for a second. Look at what we have here. We have Collidable with a single generic. So I could just rename this generic for a quick second. Let's rename it Rect, does that make the problem more obvious?

[00:05:28] Cuz now we only have Rect for Rect. Or if I rename it to Circle, we only have Circle for Circle. It should be very obvious by renaming it, we don't have the combination in there, do we? So let me go like this, oopsies, yeah, let me rename it back to T.

[00:05:44] And I'm gonna add one more in there, V. And so let's say Collidable <T> for V. But we gotta do something here, right? T is the other, V is the self. V needs to be able to check for point containing, T needs to be able to give us points.

[00:06:15] So that means I can do this, T gives me the points, V implements Contains. Okay, is anyone pretty excited about that? Is that pretty cool that we just did some generic programming here that will apply that to every single type that implements Contains and Points? That means when we go back here, look what works.

[00:06:43] We didn't have to do this cross-section of every last thing. Instead, we just need to define the basic trait behavior and then compose it and implement it for everything that implements those two traits. Is that pretty neato? I think that's pretty neato. I just wanna try to figure out how to use this all the time.

[00:07:00] I feel like I'm the hammer in search of a nail right now. Now that I know this is even physically possible, I'm just like, how am I gonna blank and implement myself out of this situation? I just think this is so neato that we can actually do these large-scale implementations that work on the struct themselves.

[00:07:17] Now, you can kind of implement the same thing in TypeScript, you just have a function that takes in the two interfaces, so you always at the lining up the correct thing. Whereas this one, you actually have the types hanging off themselves. And that means you can do these pretty cool interactions that are just not necessarily possible in other languages.

[00:07:35] For me, this is the end game, I think this is pretty a darn cool programming. It still gets even cooler than this, but we'll keep it here.