Meh. Seems pretty 1:1 to me. Skipping the complexity with runtime polymorphism[1], there's no meaningful semantic difference between a Java interface or mixin-style C++ superclass or [insert abstraction from your favorite language here].
Frankly this is one of the bits of Rust that infuriates me, because while it's not a big deal nor hard to understand, it's senselessly different from the way the rest of the world does things. It's another obstacle to new programmers, in a language that is filled with booby traps for the newbie.
[1] Let me state upfront the degree to which I am completely uninterested in debating the merits of multiple virtual inheritance vs. trait objects. They both suck. I guess if I had to pick C++ sucks a tiny bit less because you can implement something like a trait object in straightforward code, where Rust can't do vtables without boilerplating every method.
> It's another obstacle to new programmers... Why is it an obstacle rather than just something they learn? You could just as easily argue that the OO approach of wrapping up structure and functionality in a single object is equally "just another obstacle for new programmers".
> in a language that is filled with booby traps for the newbie.
That's a bit of an unfair statement, Rust has a learning curve, but it is certainly not filled with booby traps: it goes to great pains to make things transparent and be upfront about things. C++, JS or PHP are languages that I'd call filled with booby traps for beginners...
Absent evidence to the contrary, yeah. Generations of hackers have been expressing designs perfectly well with traditional class syntaxes. This is a long solved problem, and a skill you can rely on when moving between C++ or Java or C# or python or Ruby or JS (though Javascript tried to get fancy in this space too and had to bolt on traditional syntax later). But to get stuff working in rust you have to learn a different metaphor. That's bad a priori unless there's a clear advantage. And be real: there isn't, it's just syntax churn.
To wit, if it ain't broke don't fix it.
No. I have programmed in C, C++ and Rust.
Rust is much different, and it is not "just syntax ..."
“Traditional class syntaxes” have existed for only half the time since people started thinking about object-oriented programming in the 1950s— C++ was only invented in 1983, and didn’t get popular until the mid-90s. That puts it in widespread use for only one generation, and about due to be supplanted by the next major paradigm (maybe async/promises/futures).
It won’t go away, of course: structured, functional, and procedural programming are all standard tools used by most programmers today alongside object orientation. We just have enough experience with them to know what problems each is best and worst suited for, and this is what you’re seeing in Rust; it treats OOP as one useful tool in the toolbox instead of a panacea that makes everything better.
On the contrary, what Rust does is a direct counter to the most notorious pitfalls of "extends" inheritance. Generations of OO experts and advocates have gone on at great length about "has a" versus "is a" relationships, about the importance of favouring composition over inheritance, about "SOLID". But these things are only communicated by oral tradition, so they remain as booby traps for every newcomer learning to design a system. It's past time that languages did more to help those newcomers (and to be fair Rust isn't the first here: Go, Kotlin, and even Java (with its separate keyword for interfaces) all made significant progress in this direction).
Traits can be defined for existing structs without changes to the struct definition, so there are at least practical differences.
Examples here: https://blog.theenginerd.com/blog/2015/06/27/traits-on-gener...
Out of interest, do you have any examples that you would consider semantically different, while still being appropriate for day-to-day programming?
However, we don't have yet means to emulate downcasting for trait objects, except by using the Any trait (which is a footgun). Until then, class based OOP is more expressive than whatever Rust is doing now.
There is an inverse relationship between the number of contextual assumptions made about a widget and the number of contexts the widget can operate in. Something with few contextual assumptions is typically referred to as "flexible."
Duck typing as a practice entails a focus on the capabilities of a given widget rather than the "role" (read: type) of that widget. Focus on capabilities = fewer contextual assumptions. Focus on role/type = more contextual assumptions. Thus duck typing can be thought of as one possible embodiment of the koan's intended perspective.
Of course, some detractors of static typing might take this to mean that static typing implies a focus on types which as I've just shown would imply more contextual assumptions and less flexibility. But as rust's traits show, one can keep the benefits of static typing without sacrificing flexibility so long as the focus of the overall design is still capability-centric rather than role/type-centric. But that's a different conversation :)