I want to implement an OOP approach in Rust. My base class BaseClass
would look like this (but with more parameters):
struct BaseClass {
name: String,
}
impl BaseClass {
fn new(name: &str) -> Self {
Self{name: name.to_string()}
}
}
This base class has an associated trait that does the trick for inheritance:
trait BaseClassInterface {
fn as_base(&self) -> &BaseClass;
fn get_name(&self) -> &str {
&self.as_base().name
}
}
Now, I want to inherit from this base class and add extra stuff with generics:
struct MiddleClass<T> {
base: BaseClass,
value: T,
}
impl<T> MiddleClass<T> {
fn new(name: &str, value: T) -> Self {
Self{base: BaseClass::new(name), value}
}
}
Again, this is an "abstract" class. Users of my library will "inherit" from this middle class to define their structs. So let's do a trait for it:
trait MiddleClassInterface {
type Type;
fn as_middle(&self) -> &MiddleClass<Self::Type>;
fn get_value(&self) -> &Self::Type {
&self.as_middle().value
}
}
Now we implement the BaseClassInterface
trait for the MiddleClassInterface
trait and we achieved "class inheritance"!
impl<T> BaseClassInterface for dyn MiddleClassInterface<Type = T> {
fn as_base(&self) -> &BaseClass {
&self.as_middle().base
}
}
So now, users can implement their own versions of the MiddleClassInterface
and use methods from the BaseClassInterface
trait:
struct MyIntClass {
middle: MiddleClass<i32>,
}
impl MyIntClass {
fn new(name: &str, value: i32) -> Self {
Self{middle: MiddleClass::new(name, value)}
}
}
impl MiddleClassInterface for MyIntClass {
type Type = i32;
fn as_middle(&self) -> &MiddleClass<Self::Type> {
&self.middle
}
}
As MyIntClass
implements the MiddleClassInterface
, MyIntClass
will implement the BaseClassInterface
... or not?
Let's look at my main
function:
fn main() {
let my_class = MyIntClass::new("my_class", 1);
println!("{}", my_class.get_name());
}
When compiling, I get the following error:
error[E0599]: no method named `get_name` found for struct `MyIntClass` in the current scope
--> src/main.rs:71:29
|
52 | struct MyIntClass {
| ----------------- method `get_name` not found for this
...
71 | println!("{}", my_class.get_name());
| ^^^^^^^^ method not found in `MyIntClass`
|
= help: items from traits can only be used if the trait is implemented and in scope
note: `BaseClassInterface` defines an item `get_name`, perhaps you need to implement it
--> src/main.rs:13:1
|
13 | trait BaseClassInterface {
| ^^^^^^^^^^^^^^^^^^^^^^^^
For more information about this error, try `rustc --explain E0599`.
MyIntClass
implements the MiddleClassInterface<i32>
class, which in turn implements the BaseClassInterface
trait. So... why does MyIntClass
not implement the BaseClassInterface
indirectly?
Thanks in advance!
Playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=3aa8007b440b965aedf124a69dacc6f7
CodePudding user response:
Your code will work if you change:
impl<T> BaseClassInterface for dyn MiddleClassInterface<Type = T> {
to:
impl<T> BaseClassInterface for T where T: MiddleClassInterface {
What you did above is implement BaseClassInterface
for a trait object but such implementations are not considered for method lookup on concrete types. You would first have to coerce my_class
into a trait for it to work:
(&my_class as &dyn MiddleClassInterface<Type=i32>).get_name())
Whereas the fix is to not implement BaseClassInterface
for a trait object, but rather implement it for all types that implement the MiddleClassInterface
. The difference being that MyIntClass
will itself implement the base class and won't have to go through dynamic dispatch to call those methods.