While I agree with you this is not the point of a pre-RFC thread. I think we should stick to constructive criticism here.
I, too, think we should try to write more constructive responses, and I donât think a statement of âthis is not a good idea in any wayâ without any nuance really fits the discussion style for this forum, nor is it particularly convincing.
One major problem in this thread, which has been present before the latest responses, too, is the vague name âobject orientedâ for the whole idea. The term OO is so underspecified that even the Rust book, commenting on the status quo of the language has something to say about it
Many competing definitions describe what OOP is, and by some of these definitions Rust is object-oriented, but by others it is not.
This is part of the premise of a whole chapter in The Book. In some ways, Rust does share features with the paradigm(s) called âobject orientedâ, so a contrasting too general âOO is fundamentally wrongâ might even imply âRust is fundamentally wrongâ? Probably not⌠and thatâs not my point. Just, with something so vaguely delimited in its scope as âOOâ, the simplistic âOOP badâ approach is not particularly convincing. At least one should define better what isnât a good idea rather than quoting âOOâ.
Now, going into more detail like that for criticism is tedious, as that requires addressing a broad topic (either way, regarding âOOâ, or regarding the language feature in this topic). In fact, the original post of this thread probably already makes it a lot too broad to begin with, Iâd say. While this âPre-RFCâ is more concrete in what itâs actually about, than just quoting the term âobject orientedâ it still does list a fairly long list of ideas/features, all of which only have in common that they would be found in some form in more typical OOP languages, but otherwise they could probably better be (prioritized and then) discussed separately and sequentially.
Or maybe it even is envisioned as one, fully integrated, unsplittable, super large feature, though to ship that one would clearly have to answer the question of why it has to be that way.
I think it would be quite fair to strongly criticize the feature / this âPre-RFCâ e.g. on those grounds; stating that itâĹż adding too much, or too much complexity; or on the grounds of any of the specific sub-features being listed; or perhaps also because itâs not really described in much detail at all. But I donât think it leads anywhere useful to criticize the feature solely based on the name âobject orientedâ that was chosen.[1]
Iâm not saying âdonât criticize the nameâ, Iâm saying âdonât criticize the feature entirely, just because of the nameâ âŠď¸
Notice that most inheritance systems designed for bindings are very focused allowing, e.g. only to define immediate childclasses of a set of predefined classes. However I'd say that they still remain kind of cumbersome. Having put some preliminary thoughts into designing a general macro-based inheritance system, I'd say the following features would be extremly useful in such an endevor:
a) The ability to "use" fields of subtypes.
A child class can currently be modelled like this (afaik bindgen is also using such a structure to map C++ classes):
#[repr(C)]
struct Base {
field1: i32,
field2: usize
}
#[repr(C)]
struct Child {
base: Base,
field3: [u8; 12]
}
However this has the disadvantage that the base fields must then be accessed via child_instance.base.field1
. In particular with multiple class levels, this becomes cumbersome and difficult to hide away in a macro. Ideally base fields would be mirrored in the Child
-namespace, such that child_instance.field1
becomes an alias for child_instance.base.field1
, unless name collision occurs.
Since a similar feature also exist in Go and this may be usefull in other contexts, has been proposed in this context in the past (don't know where right now), it would be the most likely one to get include IMO.
Similar forwarding for methods would also be helpful, but is more tricky, when it comes to self parameter transformation and can in principle be replicated manually using today's technology.
b) Fixed layout/backcasting
Virtual methods rely on easy pointer casting a pointer to the base
member transparently back to a pointer to the childclass instance. For repr(C)
it is AFAIK guranteed that the whole struct and it's first member share the same address making such a casting trivial. This would need to be verified. However a more specific solution would be needed for multiple inheritance (not sure if one wants that) or when one wants to relace layout constrains.
c) Memory between macro calls. Macros do not allow to communicate information between each other. Such a communication would be needed to transfer base class information to child declarations. For single inheritance this can be resolved by each class generating a dedicated macro that must be used to declare children.
d) Self describing unsized types. If virtual methods are allowed, the question arises how to store boxed objects that may either be child or parent instances. Assuming each base class is attributed a trait (a hacky solution allready), trait objects may be used, but they would create some overhead in such a situation. Either one would have to call an as_base_ref()
first, or all method calls would go through a big vtable. Ideally there exist something that can be used in this context.
IMO even inheritance-based layouts have a lot of nuances in how to implement them exactly and given that their main objective is in FFI applications, I don't think that having a specific one in core language would be that helpful.
Simply adding a DerefMany
-like trait doesn't work for mutable access because it would borrow self.parent
multiple times when called directly, so a new syntax would be needed for this:
decompose Car {
Vehicle => self.parent,
Drivable => self.drivable,
RoadObject => self.parent.parent,
}
By picking the first valid match from the list there's no ambiguity when multiple structs contain members with same names (and this can be a warning).
Though that feels too magical and ambiguous, but inheritance usually is I guess. I also don't see any of the rust ecosystem actually benefiting from this besides it just making bindings nicer/easer to write.
Rust is OO enough. Inheritance is a way of implicitly attaching methods, and Rust never recommends clever implicitness.
This topicâs OP has decided they wanted to close this topic.