Live data from Hacker News

Abstraction without overhead: traits in Rust

blog.rust-lang.org

61–70 of 150 posts

Re: Abstraction without overhead: traits in Rust

#61
post #43

Earlier quoted context omitted.

I guess we'll have to agree to disagree here. I think what you consider 'pretentious jargon' really depends on what kinds of languages you've used and the way you think about them. I think calling enums unions would have been a very big mistake, as they would mislead C programmers. Even you say that they're 'quite close', but they're not the same thing.

I, uh, don't really follow that logic. A Rust enum isn't even "quite close" to a C enum, yet you used a colliding term. Surely a C programmer who would have been mislead about the fact that a union type is runtime-tagged and checked is going to be very misled that an "enum" has no numeric value and can contain variable state at runtime.

  >  A Rust enum isn't even "quite close" to a C enum
This is incorrect. The following Rust enum compiles down to a single byte, whose variants are represented by the numbers 0, 1, and 2:

  enum Foo {
      Zero,
      One,
      Two
  }
You can even give them all numeric values explicitly:

  enum Bar {
      Ten = 10,
      Eighty = 80,
      TwoHundred = 200
  }
And you can also just tell it where to start and let it count from there:

  enum Qux {
      Five = 5,
      Six,
      Seven
  }
If you throw the #[repr(C)] attribute on any of these then Rust will make sure to size them as C would on your particular platform (on my machine this attribute inflates them from 8 bits to 64 bits), making them usable directly from C as well.

Re: Abstraction without overhead: traits in Rust

#62
post #57

Earlier 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…

  > it seems like Rust has caught itself up in an internal 
  > rush (among its rock-star language nerd designers) for 
  > Masterpiece Status and sort of forgotten the goal of 
  > creating a practical tool for working programmers
This is complete hogwash. Just because you disagree with the chosen terminology doesn't justify attacks on the character of the Rust developers.

Re: Abstraction without overhead: traits in Rust

#63

Typeclasses benefit from the same "zero runtime overhead" in Haskell (but not Scala). This is particularly important in non-strict languages where inlining is a more prominent aspect of making code performant. Fortunately, GHC is a lot easier to understand WRT optimizations than gcc. You revert to the OOP-style vtables if existential quantification is introduced because you have to pack code with the data, rather tha…

  > 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

Re: Abstraction without overhead: traits in Rust

#64
post #5

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…

Does it have to be 16 bytes? I could imagine code opting in to compressed pointers (with alignment/heap restrictions). And you'd only need enough vtable bits to cover all unique types right? But imagining is easier than implementing of course. It would require recompiling all code to use the same pointer style, but whenever unwind-free Rust exists, we'll already need different compilation options for everything anywa…

The compiler could even choose the say 256 most popular vtables and use this optimization for those. Not sure how sane that would be.

Re: Abstraction without overhead: traits in Rust

#65
post #57

Earlier 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…

> and sort of forgotten the goal of creating a practical tool for working programmers

This is blatantly false. I've been in touch with Rust development for quite some time, and pragmatism has been paramount in all design decisions. Suggesting otherwise on the basis of disagreeing with some naming choices is complete nonsense.

Re: Abstraction without overhead: traits in Rust

#66
post #62
post #57

Earlier 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…

> it seems like Rust has caught itself up in an internal > rush (among its rock-star language nerd designers) for > Masterpiece Status and sort of forgotten the goal of > creating a practical tool for working programmers This is complete hogwash. Just because you disagree with the chosen terminology doesn't justify attacks on the character of the Rust developers.

Sigh... it's an opinion. I even used "seems". I was around when we all watched C++ go from "exciting new tool we should all use" to "wait, does anyone else understand that new stuff because I don't anymore". This feels exactly the same.

I'm no dummy, yet Rust is just confusing as hell sometimes. And you guys frankly don't seem to care (again: note marker "seems" to indicate a personal opinion and not a "character attack"). That turns me off. It turns lots of people off. And I don't see any significant effort being made at making it an easy tool to learn and use.

Re: Abstraction without overhead: traits in Rust

#67
post #58

Earlier quoted context omitted.

Yes. The feature has been proposed for Java too: "defender methods". Originally I resisted them on the grounds of not being necessary, but they're used all over now. Being able to supply a default implementation is extremely useful.

default method implementations makes traits behave like mixins, it's very useful (and it is a form of code reuse).

Totally agreed. I was wrong to resist them :)

Re: Abstraction without overhead: traits in Rust

#68
post #63

Typeclasses benefit from the same "zero runtime overhead" in Haskell (but not Scala). This is particularly important in non-strict languages where inlining is a more prominent aspect of making code performant. Fortunately, GHC is a lot easier to understand WRT optimizations than gcc. You revert to the OOP-style vtables if existential quantification is introduced because you have to pack code with the data, rather tha…

> 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 what makes Haskell nice today happened because it had a decade to gestate without the demands of industry coming first. Applicative wasn't discovered until 2008. Those discoveries are a big part of why I happily use Haskell for work today.

This is emphatically not a value judgment, I will likely not use Rust in anger so I'm not your customer anyway at least WRT the programming language. Representations of linear types embeddable in dependently typed languages (such as Brady is figuring out in Idris) will probably be the next step.

In my ideal universe, there's a language for people to experiment with theoretical models and practical applications of linear typing such as Idris provides for DTPLs. This is particularly appealing as it could enable programmers to define their own linearly typed models for the compiler to enforce.

Re: Abstraction without overhead: traits in Rust

#69
post #60

Earlier quoted context omitted.

Does it have to be 16 bytes? I could imagine code opting in to compressed pointers (with alignment/heap restrictions). And you'd only need enough vtable bits to cover all unique types right? But imagining is easier than implementing of course. It would require recompiling all code to use the same pointer style, but whenever unwind-free Rust exists, we'll already need different compilation options for everything anywa…

I don't try think that's feasible. The vtable is stored in the binary's data block, so that's usually a pretty compact region of memory, maybe even referenceable by 16 bits. Except that's no longer true if the type's impl for the trait is defined in a shared library. It should be possible as a compiler option if you are compiling every object that you link to and statically linking them into a single binary. Even the…

Would there be a hit? C++ has to do this:

object pointer -> load vtable -> load function pointer

while a compressed fat pointer would be like:

fat pointer -> shift to extract vtable index -> shift to make into a global table offset -> load global table base from a global variable -> load function pointer

Two loads in both cases, but in the compressed case the first one will almost always be cached; more code bloat, but not that much, and you save a bit of memory in the objects themselves.

Re: Abstraction without overhead: traits in Rust

#70
post #66
post #62

Earlier quoted context omitted.

> it seems like Rust has caught itself up in an internal > rush (among its rock-star language nerd designers) for > Masterpiece Status and sort of forgotten the goal of > creating a practical tool for working programmers This is complete hogwash. Just because you disagree with the chosen terminology doesn't justify attacks on the character of the Rust developers.

Sigh... it's an opinion. I even used "seems". I was around when we all watched C++ go from "exciting new tool we should all use" to "wait, does anyone else understand that new stuff because I don't anymore". This feels exactly the same. I'm no dummy, yet Rust is just confusing as hell sometimes. And you guys frankly don't seem to care (again: note marker "seems" to indicate a personal opinion and not a "character att…

> And I don't see any significant effort being made at making it an easy tool to learn and use.

Just to name a few off the top of my head:

1. Lots of focus on friendly compiler error messages, including typo correction, automatic lifetime suggestions, and error message explanations.

2. A strong worse-is-better approach in many aspects of the language design, such as preventing reference-counted cycles (we don't try to), numeric overflow (we don't try except in debug mode), typeclass decidability (it's deliberately undecidable in corner cases to avoid complex rules), prevention of deadlocks (we don't try), userland threads (we don't implement them anymore), asynchronous I/O (it's out of the domain of libstd for now), etc.

3. Blog posts like this one to introduce aspects of Rust, as well as the tutorial.

4. The Cargo package manager, as well as crates.io.

5. Naming conventions designed to fit well with C, for example choosing "enum" over "data"/"datatype" as in ML, "trait" over "class" as in Haskell (since the latter means something totally different), but modified in some cases to avoid leading programmers of C-like languages astray (for example, "interface" changing to "trait"). This naming process has taken time, but I think Rust is in a pretty good place now. There are obviously disagreements as to the naming, but we can't please everybody.

Certainly we weren't perfect, but there was a lot of effort put into making Rust as easy to use as possible.

Post reply on HN