News for hackers. Stuff that matters.
Re:Abstraction without overhead: traits in Rust by haberman on Monday May 11, 2015 @02:29PM (#9526812)
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 have one major downside (pointers are twice as big), but lots of upsides:

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

    - one less indirection in the call sequence for dynamic dispatch.
I like it a lot overall, though the idea of 16-byte pointers on 64-bit architectures does make me slightly queasy.
Re:Abstraction without overhead: traits in Rust by pcwalton on Monday May 11, 2015 @02:38PM (#9526882)
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 to add it to Rust too). But I don't think it works well for multiple inheritance.

Re:Abstraction without overhead: traits in Rust by fmstephe on Monday May 11, 2015 @05:33PM (#9528002)
I apologise in advance that this may not be most appropriate place to ask this question.

I am looking for a Rust tutorial. It looks like there was one, but it was deprecated in favour of 'the book'. But 'the book' doesn't seem to have a tutorial yet.

Are there any tutorial's running through how to build some small piece of working software.

I found the Golang tutorial, where you build a very basic blog extremely enjoyable. Does Rust have anything similar?

https://golang.org/doc/articles/wiki/

Thanks

Re:Abstraction without overhead: traits in Rust by steveklabnik on Monday May 11, 2015 @05:36PM (#9528018)
The book basically has two sets of tutorials: the "Syntax and Semantics" section is a bottom-up tutorial, and the "Learn Rust" section is a project-based, more top-down one. It's true that only one chapter of Learn Rust has landed at the moment. It's basically what I'm doing right now. Should have two or three more chapters over the next few days.
Re:Abstraction without overhead: traits in Rust by fmstephe on Tuesday May 12, 2015 @04:19AM (#9530210)
Great, thanks Steve.
Re:Abstraction without overhead: traits in Rust by kalmar on Monday May 11, 2015 @11:29PM (#9529437)
What is the representation for a type like Box<Reader+Writer>? Is there an extra indirection to get each of the vtables? Is there synthesized combined trait type and just one vtable pointer?
Re:Abstraction without overhead: traits in Rust by Jweb_Guru on Tuesday May 12, 2015 @01:23AM (#9529734)
I am pretty sure you can't actually construct such a type (at least not at the moment) so Rust sort of sidesteps the question.
Re:Abstraction without overhead: traits in Rust by Manishearth on Tuesday May 12, 2015 @06:03AM (#9530570)
IIRC you can.
Re:Abstraction without overhead: traits in Rust by Rusky on Tuesday May 12, 2015 @09:07AM (#9531409)
I get this when I try something like `as Box<Read+Write>`

    error: only the builtin traits can be used as closure or object bounds [E0225]
Re:Abstraction without overhead: traits in Rust by Manishearth on Tuesday May 12, 2015 @09:32AM (#9531582)
TIL; thanks :)
Re:Abstraction without overhead: traits in Rust by detrino on Monday May 11, 2015 @04:10PM (#9527508)

    >- 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 great talk about this: http://channel9.msdn.com/Events/GoingNative/2013/Inheritance...
Re:Abstraction without overhead: traits in Rust by comex on Monday May 11, 2015 @07:10PM (#9528459)
This only works for callables, though; if you want a virtual dispatch wrapper for some other set of methods, you have to write it yourself (right?). On the other hand, if you do have a relevant base class and you know the exact type, you can avoid virtual calls in C++ just by declaring the relevant methods final, although the vtable pointer will still bloat the struct a bit.

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 by humanrebar on Monday May 11, 2015 @04:25PM (#9527606)
C++ is Turing-complete, so you can do whatever you want. What Rust offers is an hierarchy free (i.e., no inheritance) polymorphism mechanism as a core part of the language.
Re:Abstraction without overhead: traits in Rust by detrino on Monday May 11, 2015 @04:29PM (#9527624)
The turing-complete argument makes no sense here.
Re:Abstraction without overhead: traits in Rust by eli_gottlieb on Monday May 11, 2015 @10:07PM (#9529165)
I think he means that, through templates, the C++ type system is Turing-complete.
Re:Abstraction without overhead: traits in Rust by steveklabnik on Monday May 11, 2015 @10:34PM (#9529246)
Rust's is too.
Re:Abstraction without overhead: traits in Rust by kibwen on Monday May 11, 2015 @11:45PM (#9529488)
Though the skimpy recursion limit makes it fairly impossible to do anything interesting with this property, much to the chagrin of some people. :P
Re:Abstraction without overhead: traits in Rust by humanrebar on Monday May 11, 2015 @05:21PM (#9527939)
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 easily.

Re:Abstraction without overhead: traits in Rust by geofft on Monday May 11, 2015 @07:12PM (#9528470)
No, Turing-completeness does not mean you can "do whatever you want". It means you can compute whatever you want that is computable. If the way you want to compute things is to embed a Rust compiler, yes you can do that, but there are many things that we'd colloquially refer to as "do whatever you want" that apply to the semantics of C++ itself.

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 by humanrebar on Monday May 11, 2015 @09:04PM (#9528919)
> 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.

Sure you can, for some value of base class and cast. You might not be able to use particular keywords, but data is data and math is math. You might not be able to use the built-in type system to do it.

C and javascript programmers write their own inheritance-based type systems all the time. And anything you can do in C, you can do in C++ if you really want to.

Re:Abstraction without overhead: traits in Rust by marrs on Tuesday May 12, 2015 @08:15AM (#9531132)
JS is a bad example because people who do that are preferring inheritance to composition, which is absurd.

In the case of C, yes you can do that, and where performance is critical you might prefer to do it, but it's obviously not going to be your first choice if you have any sense.

Re:Abstraction without overhead: traits in Rust by jahewson on Monday May 11, 2015 @05:36PM (#9528021)
That has nothing to do with Turing-completeness. There are Turing-complete languages which don't even have pointers.
Re:Abstraction without overhead: traits in Rust by MichaelGG on Monday May 11, 2015 @05:06PM (#9527849)
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 anyways, right?

Though the added complexity could offset the 16-to-8 byte size reduction.

Re:Abstraction without overhead: traits in Rust by adrusi on Monday May 11, 2015 @06:08PM (#9528180)
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 then there's be a performance hit.

Re:Abstraction without overhead: traits in Rust by comex on Monday May 11, 2015 @06:49PM (#9528362)
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 by kzrdude on Monday May 11, 2015 @06:26PM (#9528261)
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 by skybrian on Monday May 11, 2015 @03:11PM (#9527157)
This is also how Go does it. It looks like Rust traits can do everything Go interfaces can do, and more?
Re:Abstraction without overhead: traits in Rust by pcwalton on Monday May 11, 2015 @03:29PM (#9527261)
More or less, yeah. The main missing features are that downcasting isn't implemented at present (since Rust's other features remove the need for most of it), and implementation is explicit instead of implicit (a design decision which I've elaborated on in the past).
Re:Abstraction without overhead: traits in Rust by keeperofdakeys on Monday May 11, 2015 @07:10PM (#9528457)
There are some key differences, like Rust traits using generics, and Go interfaces using an empty interface for generic code.
Re:Abstraction without overhead: traits in Rust by Manishearth on Tuesday May 12, 2015 @06:08AM (#9530588)
Go's interfaces have these additional features:

- They auto-implement on types that have the appropriate methods (a feature that probably is unwanted in Rust) - They can be runtime downcasted via `.(type_name_here)` - They can be runtime type switched via `switch foo.(type)`

I'm not sure of the vtable internals though.

Re:Abstraction without overhead: traits in Rust by asdasfsdfsdf on Tuesday May 12, 2015 @02:39AM (#9529911)
The other major downside being the extra level of indirection, which could potentially cause an extra cache miss per method call. A drop in the park bucket compared to most other languages, but a serious consideration for many of the areas that C and C++ dominate.
Re:Abstraction without overhead: traits in Rust by haberman on Tuesday May 12, 2015 @04:01AM (#9530142)
Extra indirection? There is less indirection.

With the C++ approach the data is one pointer away but the vtable is two pointers away.

With the Rust approach both data and vtable are just one pointer away.

Re:Abstraction without overhead: traits in Rust by fgsfdsfgsfds on Tuesday May 12, 2015 @04:28AM (#9530239)
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 sized) pointers, to god knows where in heap memory... suffering a cache miss on every access, on average.
Re:Abstraction without overhead: traits in Rust by robinei on Tuesday May 12, 2015 @04:37AM (#9530273)
Since you are talking about a heterogeneous [edit: homogeneous] array, 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).
Re:Abstraction without overhead: traits in Rust by fgsfdsfgsfds on Tuesday May 12, 2015 @04:57AM (#9530351)
> 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 additional identifying information? I'm assuming that in this optimized case, there's a hidden type field in each struct, that it would use to index into a table of vtable pointers? If so, there you go, that's yet another level of indirection.

Re:Abstraction without overhead: traits in Rust by robinei on Tuesday May 12, 2015 @05:59AM (#9530561)
I meant to say homogenous array, which I assume you were talking about.
Re:Abstraction without overhead: traits in Rust by fgsfdsfgsfds on Tuesday May 12, 2015 @10:09AM (#9531837)
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 by haberman on Tuesday May 12, 2015 @05:43PM (#9535141)
> 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.

No, it's assuming that a "full pointer" is the only way to get a reference to a polymorphic object. This is true in both C++ and Rust.

> What about if you want to stick a bunch of objects in a vector, and iterate over them linearly?

You can do this in both C++ and Rust. But you can only put a bunch of objects in a vector if they have a statically-known type. The point of dynamic dispatch is calling methods of an object where you don't statically know its type.

Re:Abstraction without overhead: traits in Rust by GolDDranks on Tuesday May 12, 2015 @07:58AM (#9531032)
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.
Re:Abstraction without overhead: traits in Rust by fgsfdsfgsfds on Tuesday May 12, 2015 @09:55AM (#9531754)
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 by haberman on Tuesday May 12, 2015 @05:48PM (#9535175)
> You could either have objects of the same type, but with virtual functions

If objects have the same type, which is statically known, there is no need for virtual functions because the compiler can resolve the specific method implementation at compile-time.

> 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

You can do this in both C++ and Rust easily, with equivalent efficiency in both. In Rust you would just use an enum type. This design is generally highly discouraged though, because it requires callers to be aware of all possible "derived classes".

My comments were only about the case where you are using true language-level polymorphism.

Re:Abstraction without overhead: traits in Rust by rwj on Monday May 11, 2015 @03:56PM (#9527441)
This is also how interfaces are implemented in Go.
Re:Abstraction without overhead: traits in Rust by nostrademons on Monday May 11, 2015 @04:06PM (#9527492)
And typeclasses in Haskell as well.
Re:Abstraction without overhead: traits in Rust by evanpw on Monday May 11, 2015 @05:17PM (#9527911)
I don't think that's true. Dictionary-passing (or something equivalent) attaches the table of methods to the functions which take typeclass instances as arguments (with extra, hidden arguments), while Rust's Box<Trait> attaches the table of methods to the object pointer itself. So you can have a list of Box<Show> in Rust, but you can't have a list of Show in Haskell. You have to set up the extra indirection yourself using something like this: https://wiki.haskell.org/Existential_type#Dynamic_dispatch_m....

I'm not an expert in Rust or Haskell, though, so I welcome corrections.

Re:Abstraction without overhead: traits in Rust by bodhi on Tuesday May 12, 2015 @02:10AM (#9529833)
> but you can't have a list of Show in Haskell

I'm not sure exactly what you mean, but my naive interpretation is that you can:

    Prelude> :t map show
    map show :: Show a => [a] -> [String]
Re:Abstraction without overhead: traits in Rust by kimundi on Tuesday May 12, 2015 @03:44AM (#9530091)
That's not a list of Show, that's a list of a single type that instantiates Show.

The difference being that in your code you can only put in a single type at a time, eg [Int] or [String], but not both Int and String under the common Show interface type.

Re:Abstraction without overhead: traits in Rust by bodhi on Tuesday May 12, 2015 @07:07AM (#9530813)
Aha, thanks (to twic also). Now I get it!
Re:Abstraction without overhead: traits in Rust by dbaupp on Tuesday May 12, 2015 @03:36AM (#9530067)
I'm not exactly sure what they mean, since the following works, and is pretty much a drop-in replacement for a Box<...> trait object in Rust.

  data Showable = forall a. Show a => Showable a
This allows for

  [Showable 1, Showable "foo", Showable 'x']
(It requires the ExistentialQuantification language feature.)
Re:Abstraction without overhead: traits in Rust by evanpw on Tuesday May 12, 2015 @08:48AM (#9531307)
Right, this is exactly what I meant by setting up the indirection yourself. The main difference seems to be that in Haskell, you have to write this code separately for each typeclass, and wrap/unwrap it manually. In Rust, you just change Show to &Show and you get dynamic dispatch with no extra code. Here's a gist of the two approaches: https://gist.github.com/evanpw/89d89aae1159c608c476. One other difference is that in the Haskell showStatic, static dispatch is not a guarantee, just an optimization.
Re:Abstraction without overhead: traits in Rust by bodhi on Tuesday May 12, 2015 @10:21PM (#9536383)
This works on single instances, but I'm trying to see how it plays out with lists of `Bar`. As far as I can see, you need to use `Box`: https://play.rust-lang.org/?code=struct%20Foo%3B%0Astruct%20...

Which seems to me to be effectively the same as `Barrable` in this case, although it's more generic.

Re:Abstraction without overhead: traits in Rust by bodhi on Wednesday May 13, 2015 @03:05AM (#9537279)
> As far as I can see, you need to use `Box`

Which is exactly what you said in the first place!

> So you can have a list of Box<Show> in Rust, but you can't have a list of Show in Haskell.

But isn't this comparing two different things? You can't have a list of trait/typeclass in either language (excuse the pseudo-syntax):

    Rust: [Bar]
    Haskell: [Bar a => a]
But you can (with some extensions in Haskell) have:

    Rust: [Box<Bar>]
    Haskell: [Box Bar]
Re:Abstraction without overhead: traits in Rust by twic on Tuesday May 12, 2015 @03:44AM (#9530088)
That takes a list of a, where a is some type which is Show. You can't have a grab-bag of different Show things in there.