I would like to call function like get_name on Parent trait (Animal) to avoid duplicate code for different trait that has the same method. And also, I would like other struct implement child's trait only. In my example, that is we implement name() for Dog and Cat, and Animal's name is automatically implemented.
If there's only one subtrait, there's a good way: impl<T: SubTrait> SuperTrait for T. But it doesn't work with multiple subtraits as there would be overlapping implementations in the case of a type that implemented multiple subtraits
Regardless of the approach, you don't want to have the same method names for a subtrait and supertrait. Multiple trait methods with the same name and signature will be considered ambiguous regardless of any subtrait/supertrait relationship
There's no need for print_name to know anything about animals, inheritance, or anything complicated like that. State the constraints you need, and program against interfaces.
Since Animal already declares the method name(), the sub-traits don't need to (and shouldn't/can't) override it in their trait declarations. In other words, you can't implement name() for the Dog or Cat traits because they inherit from Animal and name() belongs to Animal.
And yes.. you can model hierarchical inheritance. Hierarchical behavior inheritance is easy in Rust, but state inheritance is more involved, but still very much possible. If you're curious about modeling both state and behavior inheritance, you can take a look at this post on stackoverflow. However, I wouldn't recommend modelling state inheritance if you can avoid it, and you nearly always can. Only the concrete imlementors (structs that implement the traits) need to have state in the vast majority of situations.
That is ideal solution, but with this I need a breaking change, and it also means we need to implement Named and Dog for one dog struct which is adding complexity for other building their own struct
For internal code usage, I agree with this.
What I'm building is the trait for third party user. We define the trait, and they define their struct.
It is fine if they need to implement multiple trait for their struct, but it is really nice if they can care about the single trait with everything they need.
But, I think how our design also effect simplicity for the user, maybe we need to rethink your trait design.
When you define a trait with trait MyTrait: MySuperTrait { ... } you are declaring that a MyTrait is also a MySuperTrait. Which means that anything that implements MyTrait will also have to declare an impl for MySuperTrait. This doesn't necessarily have to become complex or encumbering. Much of the behaviors could be implemented within the traits themselves, so implementing the supertrait could be as simple as impl MySuperTrait { }, which will satisfy the inheritance constraint MyTrait: MySuperTrait. One real-world example off the top of my head for a trait that has a super-trait would be DoubleEndedIterator: Iterator.
You can't really make both Cat and Dog traits implement (be a sub-trait of) Animal in the OOP sence, because unlike in other languages, there is nothing preventing a type implementing both Cat and Dog. What should then happen when you put that type into a Box<dyn Animal>?
One way to solve this would be to crate a general Dog struct that implements Animal and have every individual dog struct (like Husky for example) include that Dog struct. Then, when implementing Animal for the Husky scruct, just use the Dog's implementation. This is a ton of copy-pasted code, but it can be auto-generated with a macro.
I'm not sure what this has to do with boxing, but it's perfectly fine to put a value in a Box even if its type implements many traits, and you can also have multiple supertraits for a dyn Trait.
I was sliglty misunderstanding OP's quesrion (I think), I thought what they wanted is to have a trait Dog: Animal with default implementation of get_name returning "dog", such that any struct implementing Dog does not have to re-define get_name and it will return "dog" for that struct, even when it is in a Box<dyn Animal>.
You could do this with one trait with
impl<T: Dog> Animal for T {
pub fn get_name(&self) -> &str {
"dog"
}
}
but not with two (or more) traits. This also doesn't allow any Dog implemetations to override this default name, but there are ways around that, like adding a get_dog_name method to Dog that returns an option with default inplemetation returing None and then the get_name impl would try to use that.