A model for trait-based inheritance in Rust
| //! Demo of static "inheritance" | |
| //! | |
| //! Use trait objects to get dynamic inheritance, | |
| //! but casting to a subtype is not explored here | |
| //////////////////////////////////////////////////////// | |
| // Define Base type, interface, and what is overloadable | |
| //////////////////////////////////////////////////////// | |
| /// The main type that will be extended | |
| struct Basetype<E:BaseExtend> { | |
| data: usize, | |
| ext: E, | |
| } | |
| /// Additional data and overloadable functions for subtypes | |
| trait BaseExtend : Sized { | |
| /// the "overloadable" function from the base interface | |
| fn sub_info(s:&Basetype<Self>) -> String { String::from("None") } | |
| } | |
| /// Convenience for code clarity | |
| /// | |
| /// allows: fn foo<B:BaseInterface>(b:B) {} | |
| /// instead of: fn foo<E:BaseExtend>(b:Basetype<E>) {} | |
| trait BaseInterface { | |
| fn get_data(&self) -> usize; | |
| fn sub_info(&self) -> String; | |
| } | |
| impl<E:BaseExtend> BaseInterface for Basetype<E>{ | |
| /// not overloadable, always inherited | |
| fn get_data(&self) -> usize { self.data } | |
| /// call the overloadable version | |
| fn sub_info(&self) -> String { E::sub_info(self) } | |
| } | |
| impl BaseExtend for () { | |
| // not including functions uses the default defined above | |
| } | |
| /// non-extended version of the base type | |
| type Base = Basetype<()>; | |
| impl Base { | |
| /// function unique to the base type | |
| fn new(a:usize) -> Base {Basetype{ data:a, ext:()}} | |
| } | |
| ///////////////////////////////////////////////////////////////// | |
| // Define some subtypes and their overloaded and unique functions | |
| ///////////////////////////////////////////////////////////////// | |
| /// additional fields in the subtype | |
| struct Subtype1{ thestring: String } | |
| impl BaseExtend for Subtype1 { | |
| fn sub_info(s:&Basetype<Self>) -> String { s.ext.thestring.clone() } | |
| } | |
| /// a subtype of the base type | |
| type Sub1 = Basetype<Subtype1>; | |
| impl Sub1 { | |
| /// function unique to this sub type | |
| fn new(a:usize,b:String) -> Sub1 { Basetype{ data: a, ext: Subtype1{thestring:b}} } | |
| } | |
| /// additional fields in the subtype | |
| struct Subtype2{thenum:usize} | |
| impl BaseExtend for Subtype2 { | |
| fn sub_info(s:&Basetype<Self>) -> String { s.ext.thenum.to_string() } | |
| } | |
| /// a subtype of the base type | |
| type Sub2 = Basetype<Subtype2>; | |
| impl Sub2 { | |
| /// function unique to this sub type | |
| fn new(a:usize,b:usize) -> Sub2 { Basetype{data: a, ext: Subtype2{thenum:b}} } | |
| } | |
| //////////////////////// | |
| // Define a Sub-Sub type | |
| //////////////////////// | |
| struct Subtype<E:SubExtend>{ | |
| data: usize, | |
| ext: E, | |
| } | |
| impl<E:SubExtend> BaseExtend for Subtype<E> { | |
| fn sub_info(s:&Basetype<Self>) -> String { format!("Sub{}: {}", s.ext.data, E::subsub_info(s)) } | |
| } | |
| trait SubExtend : Sized { | |
| /// No default means this requires an implementation | |
| fn subsub_info(s:&Basetype<Subtype<Self>>) -> String; | |
| fn say_more(s:&Basetype<Subtype<Self>>) -> String { String::from("more!") } | |
| } | |
| impl<E:SubExtend> Basetype<Subtype<E>> { | |
| // `fn subsub_info` is not part of the interface | |
| fn say_more(&self) -> String { E::say_more(self) } | |
| } | |
| impl SubExtend for () { | |
| fn subsub_info(s:&Basetype<Subtype<Self>>) -> String { String::from("None") } | |
| } | |
| impl Basetype<Subtype<()>>{ | |
| fn new() -> Self { Basetype{data:0,ext:Subtype{data:1,ext:()}} } | |
| } | |
| struct Defiant{msg:String} | |
| impl SubExtend for Defiant { | |
| fn subsub_info(s:&Basetype<Subtype<Self>>) -> String { s.ext.ext.msg.clone() } | |
| fn say_more(s:&Basetype<Subtype<Self>>) -> String { String::from("I don't want to!") } | |
| } | |
| impl Basetype<Subtype<Defiant>> { | |
| fn new() -> Self { Basetype{data:51256,ext:Subtype{data:9289,ext:Defiant{msg:String::from("blah, blah")}}} } | |
| } | |
| ///////////////////////// | |
| // Show that it all works | |
| ///////////////////////// | |
| /// function that takes any base or sub type | |
| /// | |
| /// alternately, it could be defined more specifically: | |
| /// fn get_sub<E:BaseExtend>(b:Basetype<E>) -> String {b.sub_info()} | |
| /// which would allow a where clause to restrict E further | |
| fn get_sub(b:&BaseInterface) -> String {b.sub_info()} | |
| fn main() { | |
| let a = Base::new(2); | |
| println!("a base data: {}",a.get_data()); | |
| println!("a ext data: {}",get_sub(&a)); | |
| let b = Sub1::new(2,String::from("yes")); | |
| println!("b base data: {}",b.get_data()); | |
| println!("b ext data: {}",get_sub(&b)); | |
| let c = Sub2::new(2,3); | |
| println!("c base data: {}",c.get_data()); | |
| println!("c ext data: {}",get_sub(&c)); | |
| let e = Basetype::<Subtype<Defiant>>::new(); | |
| println!("e base data: {}",e.get_data()); | |
| println!("e ext data: {}",get_sub(&e)); | |
| println!("e, say more: {}", e.say_more()); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment