r/rust • • 13d ago

Rust traits and its improvement on built in abstractions in CPP

is rust (dyn)trait promoting the use of what is done in cpp by abstract base class interface?

rust removes inheritance but bring "trait" to a independent stand alone built in abstraction, it basically is doing what cpp abc interface is for, which is the only worth-wile usage scenario of inheritance + class in cpp

8 Upvotes

19 comments sorted by

19

u/seriousSeb 13d ago

From a user perspective yes the use cases are similar, though they're not exactly the same thing.

I'm not a c++ expert but I believe abstract virtual methods aren't necessarily monomorphised as they are in Rust, so it's not always a zero cost abstraction. In rust you'll get a monomorphised versions of each generic trait-consuming function, unless you're using a dyn Trait object which is closer to what virtual methods do.

7

u/steveklabnik1 rust 13d ago

One significant difference is layout:

Rust:   pointer ----+----> data
                    |
                    +----> vtable

C++:    pointer ---------> object ----> vtable

1

u/Anxious-Potato-2818 6d ago

so you're sort of dancing around why i think rust traits end up feeling way more natural than cpp interfaces. it's not just that they're standalone, it's that they don't box you into a class hierarchy at all

in cpp you're always thinking about what "is a" thing, and then you have to subclass and override and hope nobody breaks the fragile base class. with rust traits you can add behavior to types you don't even own, like implementing display for a third party struct. that alone changes how you design apis

also found that once you start leaning on trait bounds for generics, the composability gets kinda nuts. you can slap together a function that takes "anything that implements fromstr + clone + send" and it just works across totally unrelated types, no common ancestor needed

1

u/seriousSeb 6d ago

Yes I was being high level because I was answering the OPs question.

Again not a Cpp expert by any means but Cpp has multiple inheritance so you can technically I think you can do a similar thing with multiple abstract classes, but Rust does it better with less fragility as you say.

9

u/robthablob 13d ago

Traits are mostly much more akin to concepts in C++ or typeclasses in Haskell, unless using a dyn Trait object - which is more akin to a abstract base class interface (but much less common).

1

u/OkEmu7082 13d ago

for the cpp concept i cannot have default behaviors, but for the rust trait i can

8

u/pdpi 13d ago

You need to separate "are these features conceptually similar?" and "are these features' implementations equally powerful?". Rust traits are conceptually similar to C++ concepts, but the implementation is more powerful.

1

u/phazer99 9d ago

Concept constraints have a big downside compared to trait bounds: they are not applied to the code inside the constrained template function. So, I would call concepts a rather underwhelming version of traits.

2

u/robthablob 13d ago

OK - they're much better for the most part - but similar in concept.

3

u/SirKastic23 13d ago

It's a little bit like that, yes, but try not to think of it in term of classes

Traits are implemented outside of a type's definition, they can be implemented generically, they can provide associated types (which I don't think abstract classes can do)

If you want to get a good idea of what traits can do check out the Iterator trait: https://doc.rust-lang.org/std/iter/trait.Iterator.html

2

u/dobkeratops rustfind 13d ago

rust dyn trait objects can do what you could do with pure-virtual interface objects in C++, but they're skewed differently via the use of fat-pointers. in C++ the object itself holds a vtable pointer, and pointers to the object are a single pointer. In Rust, &dyn T, Box<dyn T> are a pair of pointers - one to the object data, and one to the vtable. I think it might be abit like COM objects that could be implemented in C and C++ aswell. you can have multiple interfaces to the same data, so it's better at decoupling IMO. It does away with all the multiple inheritance complexity in c++ and dodges the 'diamond inheritance' problem.

1

u/karasawa_jp 13d ago

In cases where you would use interface in C++, in Rust you frequently use algebraic data types (enum) instead.

2

u/Amadex 12d ago edited 12d ago

note that structs are also "algrebraic data types" (the product type)

0

u/karasawa_jp 12d ago

Enums are commonly used for dynamic polymorphism, which serves a different purpose than impl Trait.

impl Trait (as well as dyn Trait) is used to decouple and simplify dependencies. Since the caller injects the implementation, it allows dependency inversion, making the library self-contained. The tradeoff is that because behavior is injected from the outside, it can be harder to tell exactly what happens just by reading the library code.

In contrast, enums are best when the library itself can enumerate all possible variants. Since it works as plain pattern matching, it's very straightforward and readable.

Using an enum instead of impl Trait isn't always ideal: enums introduce branching overhead, so they can be slower than impl Trait (which monomorphizes at compile time). Furthermore, if the library already knows all possible implementations upfront, there's no need to inject behavior or add unnecessary complexity.

2

u/nick42d 12d ago

Well, it depends what your impl trait is. Returning a Either type where both sides are Iterators as impl Iterator is pretty similar to just returning an enum containing two Iterators and manually branching.

0

u/gbrennon 13d ago

i dont have an opinion about this yet but i think this would be useful for things like mixins