Skip to content

Instantly share code, notes, and snippets.

@kyleheadley
Created January 4, 2018 03:21
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save kyleheadley/6c8860d8fea67c1da2ab18abc6d556b8 to your computer and use it in GitHub Desktop.
Save kyleheadley/6c8860d8fea67c1da2ab18abc6d556b8 to your computer and use it in GitHub Desktop.
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