[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.
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
error: only the builtin traits can be used as closure or object bounds [E0225] >- 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...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.
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.
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.
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.
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.
Though the added complexity could offset the 16-to-8 byte size reduction.
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.
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.
- 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.
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.
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.
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.
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.
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.
I'm not an expert in Rust or Haskell, though, so I welcome corrections.
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]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.
data Showable = forall a. Show a => Showable a
This allows for [Showable 1, Showable "foo", Showable 'x']
(It requires the ExistentialQuantification language feature.)Which seems to me to be effectively the same as `Barrable` in this case, although it's more generic.
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]
This seems to have one major downside (pointers are twice as big), but lots of upsides:
I like it a lot overall, though the idea of 16-byte pointers on 64-bit architectures does make me slightly queasy.