There is this pattern where some functionality of an abstract class is provided by its abstract subclass, like this (TypeScript):
abstract class Foo { // could also be an interface
abstract foo(): number;
}
abstract class FooFromBar {
foo() { return this.bar() + 1; }
abstract bar(): number;
}
class Data1 extends FooFromBar {
bar() { return 5; }
}
alert(new Data1().foo()); // 6
Now I can have more classes extending FooFromBar
that use the foo=bar+1
functionality, plus more classes extending Foo
directly providing custom foo
.
Now I want the same in Rust. My intuition is that the abstract classes become traits, and concrete classes become e.g. structs implementing them - but that might be wrong, I'm quite new to Rust. And the problem is I don't know how to get foo
implemented automatically for types implementing FooFromBar
. The best I got is this:
trait Foo{
fn foo(&self)->i32;
}
trait FooFromBar:Foo{
fn bar(&self)->i32;
}
impl<S:FooFromBar> Foo for S{
fn foo(&self)->i32{self.bar()+1}
}
struct DataBar;
impl FooFromBar for DataBar{
fn bar(&self)->i32{5}
}
fn main() {
println!("{}",DataBar.foo()); // 6
}
It works, but there's a serious problem with this approach. If I add another "sub-trait", e.g. FooFromBaz
implementing foo=baz+2
like this:
trait FooFromBaz:Foo{
fn baz(&self)->i32;
}
impl<S:FooFromBaz> Foo for S{
fn foo(&self)->i32{self.baz()+2}
}
then I get an error that there are conflicting implementations:
error[E0119]: conflicting implementations of trait `Foo`:
--> src/main.rs:19:1
|
8 | impl<S:FooFromBar> Foo for S{
| ---------------------------- first implementation here
...
19 | impl<S:FooFromBaz> Foo for S{
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation
It looks to me that they are only conflicting if someone actually creates a type that implements both FooFromBar
and FooFromBaz
- that would really mean they did an unreasonable thing. But instead it fails even without any structs implementing any of them.
So the question is how to obtain a similar behaviour correctly? Is there some better generics magic to obtain that, or is it just something that should be solved completely differently in Rust and the problem is just that I'm stuck with thinking about classes?
(Question posted originally on StackOverflow: link)