8

I recently I started learning Rust and Scala and what struck me was the lack of inheritance model that I'm used to in C++ and Java.

Although I can model simple things with structs and traits in Rust, I want to see a more idiomatic way to model different kinds of problem domains using traits and composition.

For example, let's say I want to model the Observer pattern. Typically in Java we create,

class InterestingThing implements Observable {
    private String[] things;
    private Object[] observers;

    public void register(Observer o) {
       ... //register
    }

    public void notifyAll() {
       // notify
    }

    public void deregister(Observer o) {}

    // some more stuff
}

class Dude extends Observer<InterestingThing> {
    // Observer implementation
}

I'm not asking for a translation to Rust, I want to see how one would idiomatically model this Observer pattern in Rust. Any general guidelines (like all Nouns are classes) for analyzing and modeling problem domain with trait based system are welcome.

1

2 Answers 2

1

You can create a trait for Observable methods, implement the trait for your particular InterestingThing.

If you want Dude to be observable, you must also implement the trait for Dude. One thing that might work is to add InterestingThing as a struct member for Dude, or maybe you can simply contain a <T: Observable> item that you later instantiate with the InterestingThing.

It may be enough to just forward the Observable methods on Dude to the contained Observable member. This does involve some more ceremony than standard inheritance, but It is also cleaner from a design perspective. It will encourage you to think more about what interfaces are important to whom. Also, you don't have to tempt yourself with rediculous hacks like multiple inheritance. Is your Dude an InterestingThing, or can he simply be considered interesting?

A step further would be to treat Dude as a record (simply the relevant data), and create a separate ObservableDude parameterized by a <T: Observable> object.

Disclaimer: I am only vaguely familiar with Rust, but the inheritance model is quite common.

--EDIT--

It appears that Rust interfaces employ a notion of inheritance very much alike many functional languages. That is, interfaces (Traits) can inherit, not objects (structs). This can't be used in the way that you demonstrated, but it might be useful for other reasons.

If you decide you'd like to implement your pattern's machinery in multiple ways, you could create an interface hierarchy, where InterestingThing implements a strict superset of your observer methods (and, therefore, implements some subclass of Observable). Then, your Observable implementation can defer to the specific implementation of the subclass interface, while Dude only requires the Observable interface, remaining "decoupled".

0

I think inheritance models as is-a relationship like JsonSerializer is a Serializer while a trait describes what it can do, such as Car can Serialize.

Example:

trait Serialize {
    fn serialize(&self) -> String;
}

struct Car;

impl Serialize for Car {
    fn serialize(&self) -> String {}
}

Instead of having many classes that inherit from a base class I have one struct that contain a enum field which describes the variant.

Example:

enum PetKind {
    Cat,
    Dog,
}

struct Pet {
    name: String,
    kind: PetKind,
};

let cat = Pet { name: "Garfield".to_owned(), kind: PetKind::Cat };

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.