Live data from Hacker News

Abstraction without overhead: traits in Rust

blog.rust-lang.org

131–140 of 150 posts

Re: Abstraction without overhead: traits in Rust

#132

Earlier quoted context omitted.

That's assuming that a "full pointer" (i.e. an unpredictable address to an arbitrary point in heap memory) is the only way to get a reference to an object. What about if you want to stick a bunch of objects in a vector, and iterate over them linearly? In the C++ approach, the data will have a nice linear cache access pattern. In the Rust approach (unless I'm missing something) you'd be storing a vector of (double siz…

How would you put or index objects in a vector in C++ if they are virtual / dynamically sized? I'm in impression that unless you have the dynamically sized object behind a pointer, you get object slicing.

I didn't say they were dynamically sized. You could either have objects of the same type, but with virtual functions, or you could make a union of all applicable objects (thus guaranteed to be constant sized, at the size of the largest object) and store those in the array/vector, switching on a type enum or calling a function from an inherited base class.

Re: Abstraction without overhead: traits in Rust

#133

Earlier quoted context omitted.

> you would store the concrete structs continously in Rust as well. Rust would also not waste space in the vector for storing a vtable-pointer, and would instead construct fat pointers dynamically when needed (since it knows the type, it knows which vtable to inject in the fat pointer). Uh, what? How is the compiler just supposed to magically know which of the structures in the array are of what type, without any add…

I meant to say homogenous array, which I assume you were talking about.

No, I was talking about iterating over an array of objects calling their virtual functions (or either of the additional cases listed above). Of course it's easy to "do the right thing" with homogeneous arrays, either in the compiler or by hand if need be. But if you're iterating over a homogeneous array, calling the same virtual function on every single one, and your compiler somehow manages to notice this before you do, you probably screwed up in your design somewhere, so that's not the kind of problem I'm talking about.

It usually is smarter for performance to do the "data oriented design" thing and break the heterogeneous arrays into separate homogeneous arrays, so that you can potentially avoid a few levels of indirection, hoist loop invariants out, and maybe even make use of SIMD. But the whole point of the conversation was to talk about a nontrivial abstraction that (supposedly) trades performance for clarity. So I gave a scenario that would exercise that overhead.

Re: Abstraction without overhead: traits in Rust

#134
post #130
post #126

Earlier quoted context omitted.

The average case is always as expensive or less than a virtual call. But yes, the JIT most certainly introduces unpredictability, which may be unsuitable for hard realtime applications (hard realtime Java programs employ AOT compilation for those classes that require absolute predictability).

It's a problem for more than just realtime applications. Requiring a JIT means requiring a runtime, and that makes it much harder to do thing like embed Rust libraries in scripting languages or expose a C interface.

JIT has little to do with interoperation. It's quite simple to generate C symbols pointing to stubs. What makes interoperation hard is usually a GC rather than a JIT (it's just that most JITted languages also employ a GC; but if you look at, say, Go, it's just as hard to embed or link against than as and it doesn't employ a JIT at all -- its runtime "just" performs scheduling and GC).

---

BTW, Java can be embedded in scripting languages because those languages run on the platform itself and share the runtime. Because of the JIT -- that optimizes across libraries and languages -- the interoperation is cheaper than with C. So much so, that you get the following story: As part of the work being done at Oracle on Graal, HotSpot's next-gen JIT, they've ported various scripting languages to the new JIT, among them Ruby. They've found[1] that if they interpret/JIT the C code of the native Ruby extensions they get better performance than a "plain" Ruby runtime calling into statically compiled C, because the JIT is able to optimize across the language barrier.

[1]: http://www.chrisseaton.com/rubytruffle/cext/

Re: Abstraction without overhead: traits in Rust

#135
post #75

Earlier quoted context omitted.

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

> (That said, if your real complaint is that the official docs on enums are confusing, I'd certainly agree with that.) I'd love more specifics about which docs, if you have some time.

I hestiate to speak because everyone will turn it gray, but it's worth pointing out that the whole idea of enums having C-like "discriminants", which like four people have yelled at me about, is entirely missing from the book.

I had to go look it up in the reference, where it is sort of hidden too.

Re: Abstraction without overhead: traits in Rust

#136
post #93
post #7

Earlier quoted context omitted.

It also makes multiple inheritance (which Rust has, through Java-like interfaces) easy to implement, and fast at runtime. The virtual inheritance of C++ is a real mess, by contrast [1]. [1]: http://www.phpcompiler.org/articles/virtualinheritance.html Edit: I don't mean to bash C++ here, BTW; the skinny-pointer approach has a lot of benefits when all you need is single inheritance (and there are early-stage proposals…

What is the representation for a type like Box ? Is there an extra indirection to get each of the vtables? Is there synthesized combined trait type and just one vtable pointer?

You can sort of emulate this by having a trait which inherits from two other traits. (e.g: http://is.gd/SWZ9Ya)

In which case you'll have a single vtable for the "synthesized" trait object.

Re: Abstraction without overhead: traits in Rust

#137
"new traits can be implemented for existing types (as with Hash above). That means abstractions can be created after-the-fact, and applied to existing libraries."

Sounds a bit like a Ruby monkey patch. What happens in case of conflict - my trait adds a .hash method, but there already was one?

Re: Abstraction without overhead: traits in Rust

#138
post #134
post #130

Earlier quoted context omitted.

It's a problem for more than just realtime applications. Requiring a JIT means requiring a runtime, and that makes it much harder to do thing like embed Rust libraries in scripting languages or expose a C interface.

JIT has little to do with interoperation. It's quite simple to generate C symbols pointing to stubs. What makes interoperation hard is usually a GC rather than a JIT (it's just that most JITted languages also employ a GC; but if you look at, say, Go, it's just as hard to embed or link against than as and it doesn't employ a JIT at all -- its runtime "just" performs scheduling and GC). --- BTW, Java can be embedded in…

Right, GC is hard to embed and inlining is a powerful optimization. But the runtime that both JIT and GC require makes things hard for JIT on its own.

Re: Abstraction without overhead: traits in Rust

#139

"new traits can be implemented for existing types (as with Hash above). That means abstractions can be created after-the-fact, and applied to existing libraries." Sounds a bit like a Ruby monkey patch. What happens in case of conflict - my trait adds a .hash method, but there already was one?

Trait methods are only visible when the trait is in scope. So a conflict would only appear when both traits are imported and the method is called, in which case you'll have to disambiguate.

Re: Abstraction without overhead: traits in Rust

#140
post #135

Earlier quoted context omitted.

> (That said, if your real complaint is that the official docs on enums are confusing, I'd certainly agree with that.) I'd love more specifics about which docs, if you have some time.

I hestiate to speak because everyone will turn it gray, but it's worth pointing out that the whole idea of enums having C-like "discriminants", which like four people have yelled at me about, is entirely missing from the book. I had to go look it up in the reference, where it is sort of hidden too.

I just sent in this PR, is this helpful? https://github.com/rust-lang/rust/pull/25348

Specifically I ended up rewriting most of the enum page: https://github.com/geofft/rust/blob/trpl-fix-enums/src/doc/t...

I think there's more that can be done (e.g. the book doesn't document that if every variant of an enum is data-less, you can cast it to an integer), but hopefully this is a start.

Post reply on HN