Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

So, so, so much stuff in this release! The next few are shaping up to be similar. Very exciting times!

As always, happy to answer questions, provide context, etc.



In this example:

  fn foo(x: i32) -> Box<Iterator<Item = i32>> {
      let iter = vec![1, 2, 3]
          .into_iter()
          .map(|x| x + 1);

      if x % 2 == 0 {
          Box::new(iter.filter(|x| x % 2 == 0))
      } else {
          Box::new(iter)
      }
  }
Why is it that I can't return `impl Iterator<Item = i32>`? Doesn't the `Filter` type implement `Iterator` for the same associated type?


The guarantee of `impl Trait` is that if I want to call a trait method on the returned object, e.g. for iterators if I want to call `foo(0).next()`, then the function pointer will always be in the same place on the object in memory (static dispatch).

By contrast, if I returned a boxed trait, then calling the trait method requires a dynamic lookup to find the method on the boxed object, and then jumping to that function (dynamic dispatch). In this example, `iter.map(..)` and `iter.filter(..)` return two different implementations of the Iterator trait, so dynamic dispatch is required.

In general, if your function returns multiple possible implementations of a given trait, then the compiler cannot know where the trait methods will be statically, so it is impossible to do static dispatch. Since impl Trait wants to guarantee static dispatch, it requires that only one possible implementation of the trait be returned.


I think your explanation of static and dynamic dispatch is either wrong, or incredibly confusing.

The offset of the function pointers is always statically known for a given trait/interface type, it's just the actual vtable instance that may not be known, ie. the concrete type implementing that trait/interface. A statically known vtable instance that can be inlined/monomorphized is static dispatch, and if it's not known at compile-time it must be dynamically dispatched.


I don't understand what's so confusing about it. GP says

"calling the trait method requires a dynamic lookup to find the method on the boxed object"

It sounds like all you want to say is

"calling the trait method requires a dynamic lookup to find [the vtable instance, which is then used to find] the method on the boxed object"


That's not the clarification I was making. For instance, I simply can't interpret this line from the original post as anything but incorrect:

> then the function pointer will always be in the same place on the object in memory (static dispatch)

As I said, static vs. dynamic dispatch is about knowledge of the vtable instance, not about the offset into the object or the vtable. All of the offsets are always known statically, it's merely what you're indexing into that may or may not be known statically.

Maybe I'm being pedantic, but I've found that there's a lot of misunderstanding surrounding static vs. dynamic dispatch.


I see. Makes sense. It might help to show what the representation in memory of a trait object is. (I'm assuming there's a type for it somewhere in rustc, but I don't know where offhand.)



The mismatch isn’t about the associated type, it’s about the actual, underlying type. One is a Map and one is a Filter. It’s not possible to determine which is returned, so you inherently need dynamic dispatch.

There is some discussion about it possibly being sugar for an anonymous enum in the future, but that’s not what it is right now.


Is there an RFC for the anonymous enum thing? I would gladly write the implementation ... :D


I don’t believe so? I feel like there’s several internals discussions but no RFC yet.


there's some discussion, but no RFC yet. https://github.com/rust-lang/rfcs/issues/2414


I'll take a stab at explaining this in the way that I finally started grokking the issue (coming from the land of Java).

In Rust (most languages), by default the compiler needs to set aside space on the stack for each return value. So it needs a constantly known size (and shape) to create the slot on the stack. You need to flip to dynamic dispatch, ie a pointer to an object (Java's default), that will be placed on the stack as a reference to the unknown size/shape of the thing at the end of the pointer when the size/shape is unknown. A pointer always has a constant size on the stack.

In this example, `impl Trait` is just saying I want the compiler to figure out the size/shape of the thing being returned for me, and allocate that to the stack at the call site of the function. What this means is that even with `impl Trait` you must return a thing that has the same size/shape.

Steve's answer mentions a common pattern used to create constant size/shape by using an enum for the wrapper type to return two different types on the stack from the function. The only other option is to put something with unknown size behind a pointer, ie Box<Trait> or &Trait, and thus pay the expense of dynamic dispatch.


My understanding is that impl Iterator<Item = i32> in return position means that there is some single concrete type that implements the Iterator trait that we are simply not going to name. This way, the compiler can do dispatching statically. If different paths returned values with different types there wouldn't be just a single type that is known at compilation time. That is why the extra indirection via trait objects is required in the example.


I think because returning two different types requires dynamic dispatch when using the returned objects, which requires Box; if the function only returns one type then that type can be determined at runtime and further function calls can be implemented with static dispatch instead.


Rust could generate a bespoke internal proxy type.


That would still require dynamic dispatch of some kind.


But it wouldn’t require a heap allocation.


Where is the proxy implementation going to go if not the heap?


The proxy object would have statically known size (maximum of the size of the types it dispatches between, plus some metadata such as a vtable pointer or an enum discriminant). Now, because you know the size statically, you can store it in the stack.


Until then, we use `Either` (https://stackoverflow.com/a/50204370/155423)


Tangentially, do you know why Rust never included an Either type? We have Result, which is great, but Either is useful for more things than errors (though that seems to be it's main use).


We had it for a long time, as well as result, and literally no code used it, so we took it out.

There’s a crate on crates.io for it if you want to use it for something.


I feet Rust was getting pretty boring... and that's amazing! As a hobbyist I'm pretty happy to see the language maturing. I remember release 1.0 and thinking it was maybe too rushed. How wrong I was!

Is there anywhere I can get a quick recap of major features Rust is planning to eventually maybe implement, and their state? I mean major features like NLL, impl trait, etc.

IIRC: NLL are nightly already, const generics in planning stage, and what happened to CTFE? For us not in the know, a sneak peek on what's coming/being discussed is very exciting, but browsing the issue/RFC tracker is sometimes a PITA and it's hard to keep up to date, especially after getting used to the workarounds.


Yes and no! We’re gearing up for a big release, and so a lot of stuff is landing. I don’t have that text ready at the moment but rest assured that in the next couple of months you’ll see something. “2018 Edition” :)


Amazing work on this release! I've needed so many of these features in stable Rust, and I'm so excited to get to use these.

I mean, just yesterday I had to do some weird stuff to a for loop in order to iterate over all u8s in a cleanish way.

Lovely, lovely work. I'm especially excited to see how clever the ref/ref-mut `match` inferring is. If it's reliable, that's gonna remove quite a bit of friction for newcomers. So cool!


<3


With the release of impl Trait (easily the feature I've been most looking forward to), has there been any talk of/proposals to allow anonymous trait implementations? (see: https://github.com/rust-lang/rfcs/pull/2406#issuecomment-384... for an example of what it might look like)

Being able to return anonymous trait impls from inside functions could eliminate a lot of what seems like boilerplate structs/impls from Rust code. As an example, the code in futures-util that adds combinators to a Future defines a type (Then, Fuse, Map, etc) for each combinator function. With those functions now able to use 'impl Future<...>' as the return type, being able to actually type 'return impl Future<...>' could make code like that a lot less cluttered.


I’m not aware of any real proposal.


Congrats on the release, I've been following Rust for a long time.

While I'm sure it won't mess anyone up, the range for the i128 is hard to interpret. It looks like -xxxxx - xxxxxx but with the extreme number of digits it looks like two negative numbers or a subtraction problem.

[edit: u128 -> i128]


Heh, thanks. I mostly put it in there because it looks ridiculous, so that's sort of a feature, not a bug. I hear you though.


This more than makes up for a couple of slightly sparse releases. Congratulations to the whole team, and to you especially for the completion of the new book:


Congrats to the Rust team and all contributors! Awesome work!

Is the non-lexical lifetime improvements work locked to a particular release yet?

Also, now that the async/await RFC has been merged, is its implementation in nightly or stable going to be squeezed in for the 2018 roadmap plans?


async/await has been on the 2018 roadmap from the start: https://blog.rust-lang.org/2018/03/12/roadmap.html

As for non-lexical lifetimes, there's lots that's been implemented but much of it is still experimental; follow Niko Matsakis' blog series at http://smallcultfollowing.com/babysteps/blog/2018/04/27/an-a... to stay up to date with what's happening.


Yeah, the plan is for async/await to be part of the 2018 edition, but it's going to take some time.


impl Trait and the second edition of the book; very nice! Congrats on the release!

Question: Is there any way to preview what the book's typesetting looks like, since there's praise in the release note?


I am not sure to be honest, I think NoStarch has a preview chapter on the page for the book, which would show it off.


You're right, I totally missed that link. Thank you!

It shows off Chapter 3, and yes, it really looks great.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: