My wish list for Rust 2024 and beyond

I don't end up writing inheritance-like code in Rust very much, but I usually reach for this sort of pattern when I do:

struct Base<Ext:?Sized = ()> {
    value: u32,
    ext: Ext
}

struct NameExt {
    name: String
}

type NamedBase = Base<NameExt>;

impl<Ext:?Sized> Base<Ext> {
    fn get_value(&self) -> u32 { self.value }
}

impl NamedBase {
    fn get_name(&self)->&str { self.ext.name.as_str() }
}

This way, NamedBase is-a Base in a very real sense that the Rust compiler understands. It's also pretty close to how inheritance is implemented in C++. If you want various Exts to influence the behavior of Base's methods, you can define a trait for them and choose between static and virtual dispatch as appropriate for your application:

trait BaseExt { ... }
impl<Ext:?Sized> Base<Ext> { 
    fn reqires_delegate(&self) where Ext:BaseExt { ... }
}

Edit: Thinking about this some more, the most significant drawbacks to this approach are:

  1. All types are final by default, and must opt-in to be extended. This is different from C++, but feels like it fits the Rust design philosophy
  2. The extension trait can be quite awkward, especially if the method definitions need access to the base object's properties. This can be improved with something like object-safe arbitrary_self_types: This lets the trait include methods that take self: &Base<Self> and then allows them to be called via &Base<dyn BaseExt>
4 Likes