Traits. traits are similar to interfaces and other languages and they're really cool rust takes the composition over inheritance approach. Remember our red fox struct? Let's make a trait called noisy traits define the required behavior. In other words, functions and methods that a struct must implement if it wants to have that trait. The noisy trade specifies that the struct must have a method named get noise that returns a borrowed string slice if the struct wants to be noisy.
So let's add an implementation of the noisy trait for red fox. Let's take a look at how this works. We implement the noisy trait for the red fox struct. And our implementation of the required get noise method is Meow, which is all great, but why bother? We could have just implemented that method for red fox directly with without involving a trade at all, the answer is that once we have a trait involved, we can start writing generic functions that accept any value that implements the trait. This function takes an item of type t, which is defined to be anything that implements the noisy trait, the function can use any behavior on an item that the noisy trade defines.
So now we have a generic function that can take any type, as long as it satisfies the noisy trade. That's pretty cool. As long as one of either the trait or the struct is defined in your project, you can implement any trade for any struct. That means you can implement your traits on any types from anywhere, including built ins or types you import from some other package. And on your struct, you can implement any trait whether built in or from some project. Here I've implemented the noisy trait for the built in type ua, which is a byte.
So our generic print noise function works just fine with it. Now, there's a special trait called copy. If your type implements copy, then it will be copied instead of moved in move situations. This makes sense for small values that fit entirely on the stack, which is why the simple primitive types like integers, floats, and Booleans implement copy. If a type uses the heap at all, then it cannot implement copy. You can opt into implementing copy with your own type if your type only uses other copy types.
Okay, let's go through an example of where traits really seem to shine. Let's say we have a game with a goose, a Pegasus and a horse. These have several attributes in common these two can fly, these two can be written and these two explode. You can see how it could get really tricky to use object inheritance to define the correct behavior that each final object needs to have. But it's pretty straightforward with traits. The goose implements the fly and explode traits the Pegasus implements Fly and ride traits and the horse implements the ride and explode traits.
And we're all happy. It's interesting to note that traits implement inheritance. So a trait can inherit from another trait that makes it possible to have a trait inheritance hierarchy like this, where movement is the root trait, Ron inherits from movement, and so on. Let's say we have a separate damage trait hierarchy with a damage parent trait and an explode child trait, then a horse struct, the implements ride and explode also has to implement the parent traits movement run and damage. Making your trait inherit from a parent trait really just means that anyone who implements your trait is going to have to implement the parent traits as well. traits can also have default behaviors.
So if you design structs and traits carefully enough, you might not have to implement some of the trade methods at all. If you want to implement default trade behavior inside your trade definition. Instead of ending your function or method definition with a semicolon, add a block with your default behavior. Then when you implement the trait for your struct just don't provide a new definition for the method whose default implementation you want to use. The presence of an implementation will override the default. This is a fully functional example, the robot struct in the middle implements the run trait, but it doesn't override the default run method.
So when robot run is called from main at the bottom, it executes the default run method defined on the trait at the top and prints I'm running. One gotcha I really ought to mention, you can't define fields as part of trades that may be added in the future if someone can figure out the right way to do it. In the meantime, the workaround is to define setter and getter methods in your trade. In the next video, we will go over some of the collections in the standard library