Great article. The main bit of new information for me was that while Rust supports dynamic dispatch, its implementation has a noticeable difference from C++. In C++, the vtable pointer is in the object itself. In Rust, it's stored inside what is essentially a "fat pointer." Pointers to traits ("trait objects") are actually two pointers: the pointer to the vtable and the pointer to the actual object. This seems to hav…
This is also how Go does it. It looks like Rust traits can do everything Go interfaces can do, and more?
Abstraction without overhead: traits in Rust
71–80 of 150 posts
Re: Abstraction without overhead: traits in Rust
#72Great article. The main bit of new information for me was that while Rust supports dynamic dispatch, its implementation has a noticeable difference from C++. In C++, the vtable pointer is in the object itself. In Rust, it's stored inside what is essentially a "fat pointer." Pointers to traits ("trait objects") are actually two pointers: the pointer to the vtable and the pointer to the actual object. This seems to hav…
>- allows traits to be implemented for existing types, as opposed > to C++ where the type's declaration has to list all base classes. >- allows a type to be used through dynamic dispatch while allowing users > who don't need this to avoid the vtable overhead. In C++ you can have this as well. For example, std::function is able to wrap any callable type without them having to have any base classes. Sean Parent gives a…
However, I'd say Rust traits are more elegant in unifying what are two separate worlds in C++:
(1) calling virtual methods on a base class pointer, which can be (usually is) dispatched at runtime, but thus can't support methods with generic parameters or having a 'virtual type' (instead of a method), and
(2) accessing members of template parameter classes, including (possibly generic) methods, constants, typedefs, etc. - much more flexible, but doesn't work at runtime. Also dynamically typed, for better or worse; Rust thinks worse.
In Rust, (1) is a trait object, and (2) can be done with a generic parameter specified to implement a certain trait with methods, associated types, and constants. Rust's compiler doesn't try to be magic (unlike, say, Haskell), and so traits with generic methods and such can't be made into trait objects, but they're still traits - they feel like the same basic kind of thing.
Re: Abstraction without overhead: traits in Rust
#73Earlier quoted context omitted.
The turing-complete argument makes no sense here.
Sure it does. I can pass around a struct containing any bytes I want and do anything I want with it. If I want to define FatPointer that contains an opaque pointer (or some equivalent union) and a polymorphic Strategy object of some sort, I can. You can do the same in C, for that matter. Or in assembly. The reason we still come up with new languages is so that you can write code that conforms to certain patterns easi…
You cannot, for instance, create a new abstract base class and have a pre-existing class in someone else's header implement your abstract base class, such that a pointer to that pre-existing class can be dynamic_cast to your class.
Because that statement refers to C++ semantics, not your ability to compute things, it's not affected by the claim of Turing-completeness.
Re: Abstraction without overhead: traits in Rust
#74There is always overhead when adding abstractions--the only question is whether you pay at runtime or at compile time. C++ (and presumably Rust) choose the latter, Python and Go choose the former.
If you want to be really pedantic, there is also the choice between "runtime overhead" and "code overhead" (code specialization). And why not mention the overhead of the programmer foregoing these "short cuts" and hand-coding it herself.
Re: Abstraction without overhead: traits in Rust
#75Earlier quoted context omitted.
I'll have to admit enums are where I gave up the first time I read the Rust tutorial. They seem almost completely unrelated to enums in C and other languages.
Right, because they're unions. :) Just go back to that tutorial with your C hat on and substitute the word "union" for "enum" and I promise it will all make sense. All your intuition about C unions will cross over just fine, and the new Rust rules (they're tagged at runtime and the compiler enforces that you can only ever use fields of a runtype-checked subtype) are straightforward extensions. Likewise the linked blo…
This is not true. Rust enums allow you to match on which of the types you have. C unions do not. If you want to implement a switch statement over the possible members of a C union, you need to put it inside a struct with a type field. You don't need to do so in Rust, and you can't do so and have it compile.
(That said, if your real complaint is that the official docs on enums are confusing, I'd certainly agree with that.)
Re: Abstraction without overhead: traits in Rust
#76Earlier quoted context omitted.
Stop it, they're unions. :) Among the target market, people are going to intimately familiar with the use of "enum" and "union" from the C language. Rust's concept of a single object that can store exactly one of several types of sub-objects matches one quite closely, and not the other. Having a runtime tag and affirmative checking doesn't change the nature of the thing. We don't call "cars" something different when…
I don't really want to argue about enum vs. union, because as far as I'm concerned they're pretty much equally accurate or inaccurate. Enums in C carry tags, but no data. Unions in C carry data, but no tags. Rust ADTs carry both (or one, or neither). So "enum" or "union" are pretty much equally good/bad names as far as I'm concerned. Consensus was in favor of "enum", so we went with it. Swift did too, so the small am…
As does, notably, Objective-C, like Ruby due to the Smalltalk heritage.
I've never heard the term "block" used to refer to heap allocations, so I don't think it would really help.
Re: Abstraction without overhead: traits in Rust
#77Earlier quoted context omitted.
Right, because they're unions. :) Just go back to that tutorial with your C hat on and substitute the word "union" for "enum" and I promise it will all make sense. All your intuition about C unions will cross over just fine, and the new Rust rules (they're tagged at runtime and the compiler enforces that you can only ever use fields of a runtype-checked subtype) are straightforward extensions. Likewise the linked blo…
> Just go back to that tutorial with your C hat on and substitute the word "union" for "enum" and I promise it will all make sense. All your intuition about C unions will cross over just fine, and the new Rust rules (they're tagged at runtime and the compiler enforces that you can only ever use fields of a runtype-checked subtype) are straightforward extensions. This is not true. Rust enums allow you to match on whic…
I'd love more specifics about which docs, if you have some time.
Re: Abstraction without overhead: traits in Rust
#78Earlier quoted context omitted.
If you want to be really pedantic, there is also the choice between "runtime overhead" and "code overhead" (code specialization). And why not mention the overhead of the programmer foregoing these "short cuts" and hand-coding it herself.
That first one ain't pedantic; C++ code on the whole tends to generate a lot of assembly bloat with inlined methods and template instantiations, and while executable size probably isn't that important for most use cases, it is for quite a few.
Re: Abstraction without overhead: traits in Rust
#79Earlier quoted context omitted.
> Wish regions hadn't been abandoned, but it seems like > somebody wanted Rust pushed into becoming a product quickly. No idea what you're referring to here. Lifetimes are entirely based on the regions literature, and four frigging years of design iteration is hardly "pushed into becoming a product quickly". :P
Lifetimes aren't regions. 4 years when it is a recapitulation of existing technology and hasn't had time to push anything forward and we already have PLs with similar facilities? That's a product-oriented rather than research-oriented direction whether you think it's too fast or not. For comparison, Haskell dates to the early 90s, based on non-strict FP languages from the 80s. ML itself started in the 70s. Much of wh…
Re: Abstraction without overhead: traits in Rust
#80Earlier quoted context omitted.
Lifetimes aren't regions. 4 years when it is a recapitulation of existing technology and hasn't had time to push anything forward and we already have PLs with similar facilities? That's a product-oriented rather than research-oriented direction whether you think it's too fast or not. For comparison, Haskell dates to the early 90s, based on non-strict FP languages from the 80s. ML itself started in the 70s. Much of wh…
Actually, lifetimes are basically the same as regions, going back to FX87. Lifetimes in Rust are more like regions in Tofte & Talpin 94 than those in FX87, but where inter-region pointers are constrained by the lifetime of regions rather than changing the lifetimes of the regions (as required by Rust's goals for regions).
I find it profoundly disquieting when redactions are created around the history of projects, pushing the impression that the place arrived at was what was wanted all along. Our failures can inform posterity as much as our successes.[1] The core Rust team has been perfectly candid about the history of the project.
[1]: Cf. pre-Monad IO subsystems for Haskell, http://www.scs.stanford.edu/~dbg/readings/haskell-history.pd... and http://research.microsoft.com/en-us/um/people/simonpj/papers...