I see a proposal to add "virtual struct" and "virtual fn" in the workweek meeting notes, which appears to add an exact copy of Java's OO system to Rust.
I think however that this should be carefully considered, and preferably not added at all (or failing that, feature gated and discouraged). The core problem of "virtual functions" (shared by Java's classes, etc.) is that rather than exposing a single public API, they expose two: the API formed by public functions, and the API formed by virtual functions to be overridden by subclasses, and the second API is exposed in an inflexible and unclean way. A much better way of allowing to override part of a struct's behavior is by defining a trait with the overridable functionality, and allowing to pass in an implementation of the trait to the base class, while also providing a default implementation if desired. Another way is to have the "subclass" implement all the traits that the "base class" implements, include a field of the "base class" type, and then direct all non-overridden functionality to the "base class" (here syntax sugar can be easily added to eliminate the boilerplate, by automatically implementing all non-implemented trait functions by calling the same function on the base class field). These approaches can be combined, as the first approach allows to change the "inside" behavior of the base class, while the second one allows to put extra behavior "around" the base class code. The fact that OO using virtual functions (as opposed to traits) is a bad design is one of the crucial steps forward of the design of languages like Go and current Rust compared to earlier OO languages, and Rust should not go backwards on this. Here is a list of issues with virtual functions: 1. Incentive for bad documentation Usually there is no documentation for how virtual functions are supposed to be overridden, and it as awkward to add it since it needs to be mixed with the documentation on how to use the struct 2. Mishmashing multiple unrelated APIs With traits, you could pass in multiple objects to implement separate sets of overridable functionality; with virtual structs you need to mishmash all those interfaces into a single set of virtual functions, all sharing data even when not appropriate. 3. No encapsulation Private data for virtual function implementations is accessible to all other functions in the struct. This means for instance that if you have a virtual function called "compute_foo()" that is implemented by default by reading a "foo" field in the base class, then all other parts of the base class can access "foo" too. If anything else accesses mistakenly "foo" directly, which it can, then overriding "compute_foo()" will not work as expected. If compute_foo() were provided by an external trait implementation, then "foo" would be private and inaccessible, eliminating the problem. 4. Data for overridden implementations left there in a "zombie" state. In the above example, if you override "compute_foo()", the foo variable in the base class will no longer be used, yet it will still be present in the type and allocated in memory. 5. Inability to statically dispatch With a trait implementation, you can pass the concrete type as a generic parameter, allowing static dispatch. If you instead call an overridable virtual function, then you can't dispatch that statically at all (unless you add cumbersome syntax for that). 6. Adds a ton of unnecessary complexity to the language _______________________________________________ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev