News for hackers. Stuff that matters.
Re:Announcing Rust 1.27 by computerphage on Thursday June 21, 2018 @04:21PM (#17368936)
The new syntax for dynamically-dispatched traits, “dyn Trait”, is an example of Rust’s persistently excellent consideration of what should be explicit and what should be implicit. Python’s mantra of “explicit is better than Implicit” mostly captures my general feeling, but you can’t make everything explicit. Back before impl Trait existed, just Box<Trait> seemed very clear. Now that there’s an important thing to differentiate it from, it seems better to have dyn Trait and impl Trait both prefixed with keywords.

It also makes for much easier googling. ;)

Re:Announcing Rust 1.27 by sametmax on Friday June 22, 2018 @05:45AM (#17372723)
Python is my goto language but let's not forget it disrespects it's own mantra very often. Like a certain pirate would say, "it's more what you call a guideline than actual rules".

Duck typing is implicit interface.

Foo().bar() is an implicit Foo.bar(Bar()) as self is only explicit on declaration, not call.

__init__ is called and passed parameters implicitly.

__new__ is an implicit class method.

Some time the magic is nice to have. Sometime it's like yield.

Yield implicitly turns your function into a very different object, which makes everybody goes wtf the few first times.

They fixed that with async / await, but I still wish we had a "gen def" to allow yield in bodies, like we have "async def".

Re:Announcing Rust 1.27 by kbd on Thursday June 21, 2018 @04:48PM (#17369157)
> Rust’s trait object syntax is one that we ultimately regret.

Would someone please explain the problem (and the solution) for someone who doesn't know Rust yet?

Re:Announcing Rust 1.27 by steveklabnik on Thursday June 21, 2018 @04:57PM (#17369221)
The linked thread is good, but the short answer:

    Box<Foo>
If Foo is a struct, this is a single pointer to the heap. If Foo is a trait, this is a double pointer: a pointer to the data on the heap, and a pointer to a vtable for its methods. These two things are very different, but look similar. That's the mistake.

This change separates the two. Now:

    Box<dyn Foo>
is always the latter, a "trait object". We have to keep the old syntax working for stability reasons, but can lint against it, so that it nudges you into the better syntax. So in this world,

  // always a single pointer to a struct (or enum)
  Box<Foo>

  // always a double pointer
  Box<dyn Foo>
This is more clear for humans. The compiler can tell with 100% fidelity, of course. But humans aren't compilers. Different things with different costs look different.

As an addendum, the parent mentions another feature, "impl Trait", which can be used with traits. Their point was, originally, Box<Trait> was the only thing that existed like this. With "impl Trait", it was "impl Trait" vs "Trait", and is now "impl Trait" vs "dyn Trait", which is much more clear, at least in many people's opinions. :)

Re:Announcing Rust 1.27 by KwanEsq on Thursday June 21, 2018 @08:18PM (#17370262)
>We have to keep the old syntax working for stability reasons

Will Rust 2018 enable you to remove the old syntax? Or is that a bigger change than the opt-in is allowed to cover?

Re:Announcing Rust 1.27 by dikaiosune on Thursday June 21, 2018 @09:59PM (#17370711)
Rust's built-in lints are configurable with compiler directives, so you'll be able to add

    #![deny(bare_trait_objects)]
To the top of your crate and cause a compiler warning if someone uses the old syntax on your project.

EDIT: just realized I mis-parsed "you" in parent. Oh well.

Re:Announcing Rust 1.27 by steveklabnik on Thursday June 21, 2018 @08:50PM (#17370401)
This kind of change is the kind an edition can make, though I’m not 100% sure if the plan for 2018 is to warn or error on the older syntax.
Re:Announcing Rust 1.27 by lewisinc on Friday June 22, 2018 @02:20AM (#17371811)
I feel like a warning for one edition and error on the next would make it straightforward as to how much time is left to update the code.
Re:Announcing Rust 1.27 by steveklabnik on Friday June 22, 2018 @06:48AM (#17372971)
That’s generally true, the question is, do you warn in 2015 or 2018?

That being said, this particular fix is 100% automatable, and rustfix can already do it, so the time to update is “a few seconds”.

Re:Announcing Rust 1.27 by littlestymaar on Friday June 22, 2018 @05:03AM (#17372545)
IIRC, it will take two editions before removing something: at the first edition the compiler will lint against it (which is already quite a big change: you get tons of new warnings you need to fix) and one edition later, it's an error.

Edit: I didn't see the answer from steveklabnik below. Apparently, it's not clearly decided yet.

Re:Announcing Rust 1.27 by db48x on Thursday June 21, 2018 @08:37PM (#17370353)
Yes, the next edition will allow breaking changes, while still allowing you to use crates written in the older edition.
Re:Announcing Rust 1.27 by steveklabnik on Thursday June 21, 2018 @08:51PM (#17370404)
some forms of breaking changes. This is of that kind, though we cannot make arbitrary ones.
Re:Announcing Rust 1.27 by algesten on Thursday June 21, 2018 @05:03PM (#17369270)
A trait is like an interface. A struct implementing a trait means it takes on the methods of that interface (but there's more to it, because a trait can have default implementations of of the trait methods).

If you want to express something like "this is a variable that holds something that fulfils this trait", without knowing the _actual_ type it is, that variable effectively has an unknown runtime size. std::io::Read is an interface for reading bytes of some source, like a file or a socket.

This matters because we're talking about a stack frame. So the size needs to be known at compile time.

    let a: u64 = 42; // ok, because well known size.

    let b: Read = ...; // illegal, because unknown size.
A "trait object" places the object on the heap and has a pointer in its place.

    let b: Box<Read> = ... // legal, because pointer is a known size
However it's a bit more complicated, because this syntax allows for dynamic dispatch at runtime using a vtable. So there's a quite big difference between Box<u64> (a 64 bit unsigned integer on the heap) vs Box<Read> (a runtime dispatched lookup via a vtable).

This difference is not obvious at a glance though. Hence the new syntax: Box<dyn Read>.

(I think I got that right)

Re:Announcing Rust 1.27 by steveklabnik on Thursday June 21, 2018 @05:07PM (#17369303)
You did, except that it’s not one pointer, it’s two. This is one way that rust is different than C++; the vtable isn’t stored with the data, but separately, with the “trait object” itself being two pointers to the two things.
Re:Announcing Rust 1.27 by tzahola on Thursday June 21, 2018 @06:58PM (#17369932)
>This is one way that rust is different than C++; the vtable isn’t stored with the data

Which “data” is the vtable stored with in C++? An object contains only a pointer to its vtable, not the vtable itself...

Re:Announcing Rust 1.27 by stormbrew on Thursday June 21, 2018 @07:44PM (#17370129)
A C++ object with virtual methods and some members looks like this, if you put it in terms of C:

  struct ClassVTable {
   void (*firstMethod)();
   void (*secondMethod)();
  }

  struct Class {
   struct ClassVTable *virt;
   int firstMember;
   int secondMember;
  };
and a pointer to it looks like:

  struct Class *objectRef;
A language that uses fat pointers, like rust, has this separated completely:

  struct Class {
   int firstMember;
   int secondMember;
  };

  struct FatPointerToClass {
   struct ClassVTable *virt;
   struct Class *data;
  }

  FatPointerToClass objectRef; // note lack of pointer, it'd be stored on the stack directly for eg.
This means a much simpler object layout in exchange for passing around larger pointers, and it's a good fit for trait-based typing since it doesn't require you to know all possible interface subtypes of the object in order to describe or use its layout.

(please note that I'm using C struct to be precise about the concept, and this should not be taken as a perfectly verbatim description of the actual object layouts in memory)

Re:Announcing Rust 1.27 by Coding_Cat on Thursday June 21, 2018 @08:42PM (#17370372)
Oh, I didn't know that: this is also great for serializing code where you might have an array of `Class`, it would still be a POD-type to use C++ language.
Re:Announcing Rust 1.27 by steveklabnik on Thursday June 21, 2018 @07:33PM (#17370073)
My understanding is that, generally (because (again in my understanding) this is all compiler-specific, not mandated by the standard) is that in C++, there's a single pointer that points to the combo of the vtable and then the data, after it. See http://www.drdobbs.com/cpp/storage-layout-of-polymorphic-obj... for example. While a Shape is a position, outline, and fill, if you have virtual functions, the pointer points to a vtable pointer, and then all of the data.

In other words, in Rust, a pointer to a shape is

  (pointer to vtable, pointer to data)
whereas, in C++, a pointer to a shape is

   (pointer to shape)
where shape is

   (pointer to vtable, data)
If that's incorrect, I'm quite happy to be corrected! This isn't an area I'm an expert in.
Re:Announcing Rust 1.27 by tzahola on Friday June 22, 2018 @04:05AM (#17372308)
That’s absolutely correct.

I get it now, that you meant the “pointer to the vtable” under “vtable” in your original post.

Re:Announcing Rust 1.27 by steveklabnik on Friday June 22, 2018 @06:49AM (#17372974)
Ah, I see. Yes, I should probably have been more clear there. Thanks for pointing that out.
Re:Announcing Rust 1.27 by algesten on Thursday June 21, 2018 @05:10PM (#17369319)
Good to know!
Re:Announcing Rust 1.27 by int_19h on Friday June 22, 2018 @04:40AM (#17372459)
The linked port says that it has proven to be a bad design decision specifically in the contect of "impl Read" etc. Can you give an example of that, and why it was confusing before?
Re:Announcing Rust 1.27 by the_mitsuhiko on Thursday June 21, 2018 @04:51PM (#17369182)
Re:Announcing Rust 1.27 by weavie on Thursday June 21, 2018 @05:19PM (#17369382)
Reading up an static vs dynamic dispatch may help - https://en.wikipedia.org/wiki/Dynamic_dispatch.

Essentially Box<Foo> is static dispatch. You know at compile time which exact methods you are going to call. Box<dyn Foo> is dynamic dispatch. Since Foo is a trait, at compile time you won't know which methods are called, it depends on the type of the object passed in (as long is it implements the Foo trait).

Re:Announcing Rust 1.27 by TheCycoONE on Thursday June 21, 2018 @05:46PM (#17369562)
Your example is misleading. If Foo is a trait Box<Foo> and Box<dyn Foo> are the same thing. Impl Foo is for static dispatch.

Illustrating the need to dump the old unqualified syntax.

Re:Announcing Rust 1.27 by weavie on Thursday June 21, 2018 @06:28PM (#17369794)
Ah ok, I'm back to being confused again then!

Is this right:

If Foo is a struct then Box<Foo> is static dispatch. If Foo is a trait Box<dyn Foo> is dynamic dispatch, but then so is Box<Foo> - but that is the regrettable point. So from now on, if Foo is a trait we should be writing Box<dyn Foo>, which is largely syntactic sugar to make the situation more clear?

I don't fully understand what the release notes are saying about using impl Trait. When would you use Box<impl Foo> vs Box<dyn Foo>?

Re:Announcing Rust 1.27 by steveklabnik on Thursday June 21, 2018 @06:35PM (#17369828)
You are right.

It’s not that you would use Box<impl Foo>, but when taking about the two features, it’s easier to compare them, as they’re actually distinct syntactically.

Re:Announcing Rust 1.27 by weavie on Friday June 22, 2018 @04:00AM (#17372289)
Thankyou.