Hacker News new | ask | show | jobs
by pottereric 3448 days ago
Forgive my ignorance. What do Rust and Go call their data structures that are analogous to classes?
3 comments

There really isn't a direct analog in Rust. The best approximation in Rust is actually a combination of constructs in the language: structs and traits.

Structs are almost exactly like the C-objects of the same name: they define layout in memory for a data structure with discrete types as fields, and there are various packing options available. However in addition to this a struct-specific implementation can exist which defines static and instance based methods. As there is no inheritance in Rust, a struct's "impl" is unique to itself.

Polymorphism is implemented via the traits system. A trait defines a set of methods that objects meant to have said trait must implement (unless a default implementation for a given method in the trait exists). Any struct can have an implementation for a given trait, so these can be considered a (rough) analog to abstract base classes/interfaces (e.g. C++ classes that are either pure virtual or have virtual functions with default implementations).

You say traits are a rough analogue for C++ base classes with virtual methods. What are the fundamental differences?
For one, traits are inherently static and their methods are statically dispatched, somewhat like a concept. Traits which satisfy certain conditions which make them "base class-like" can be reified as a trait object, which is a vtable ptr + data ptr pair, which dynamically dispatches through the vtable. The separation of the vtable ptr from the object means every trait object gives you dynamic dispatch for one trait only and you can have an unbounded number of trait objects, in contrast to C++ where you have dynamic dispatch through the finite number of base classes listed by the class author.
I'm not familiar enough with the internals of how Rust handles v-tables and the like in light of its other features to answer that competently.

In practice, one defines an interface in C++ by having a (hopefully) stateless class with pure virtual method declarations, and then classes derived from this class must implement these methods (in order to instantiate them anyway). In Rust one defines a data structure (either a struct or enum) and then, separately, writes the "impl" for it for a trait.

The big difference is that when you have a trait object in Rust, it's a double pointer: a pointer to the vtable, and a pointer to the data. In C++, in my understanding, you'd have a single pointer to both the vtable and data laid out next to each other.
Thanks for that.

What you describe for C++ is typically the case (and I've written completely unsafe, non-portable code that exploits this fact before), but I'm unsure whether that's required by the standard.

So rust traits are effectively/functionally abstract classes with restricted functionality and a different implementation?
Sort of. The closest thing in C++ would be concepts. That is, even using traits in this way is the minority case; they're more usually used for monomorphized, statically dispatched code.

But, given the case where you want dynamic dispatch, then in a sense, they are, yes.

The whole point is to unbundle what classes are, so there is no direct analogy. Structs are one piece of traditional classes; traits (rust) or interfaces (go) are another part, and delegation a third part.
Well, in Go they are just Types