News for hackers. Stuff that matters.
Re:Zero-cost futures in Rust by losvedir on Thursday August 11, 2016 @12:19PM (#12269496)
I dabbled with rust in the past and was really fascinated with it, but haven't played around lately. One thing caught my eye in the post:

    fn get_row(id: i32) -> impl Future<Item = Row>;
That return type looks odd to me. What does it mean to return an "impl", and is that a new feature in rust, or just something advanced that I missed in my exploration before?
Re:Zero-cost futures in Rust by aturon on Thursday August 11, 2016 @12:25PM (#12269552)
This is an exciting upcoming feature in Rust, which you can read more about in a couple places:

- http://aturon.github.io/blog/2015/09/28/impl-trait/ - https://github.com/rust-lang/rfcs/pull/1522

This feature allows you to return any struct that implements the trait, without having to type the name of the struct (which can sometimes be quite big). It also means that clients only know what traits are implemented; the concrete type is invisible to them. The above links have a bunch more detail.

(And this feature is set to land in nightly Rust very soon! Shoutout to eddyb :)

Re:Zero-cost futures in Rust by dukerutledge on Thursday August 11, 2016 @01:40PM (#12270225)
So, existentially quantified types?
Re:Zero-cost futures in Rust by GolDDranks on Thursday August 11, 2016 @01:58PM (#12270358)
Yes. However, there isn't full support of existential types in the horizon – only returning them from a function (and using them inside the caller, as the type inference allows this). This resolves some specific pain points of the current Rust experience.

There may or may not be some extensions (like storing them in fields of structs) in the future.

Note that Rust supported existentials before too, in the form of trait objects. But this was only possible behind a pointer and using virtual dispatch, so the performance story wasn't perfect. The impl Trait syntax is supported through monomorphization.

Re:Zero-cost futures in Rust by dukerutledge on Thursday August 11, 2016 @02:00PM (#12270379)
Yeah, since rust is eager existential types seem like a necessary evil.
Re:Zero-cost futures in Rust by agmcleod on Thursday August 11, 2016 @01:07PM (#12269933)
Sounds awesome. Love it :)
Re:Zero-cost futures in Rust by Ygg2 on Thursday August 11, 2016 @01:06PM (#12269928)
Rust couldn't return Traits since they vary in return Size.So using couple of chain and maps would result in following syntax.

    fn did_too_many_chains() -> Chain<Map<'a, (int, u8), u16, Enumerate<Filter<'a, u8, vec::MoveItems<u8>>>>, SkipWhile<'a, u16, Map<'a, &u16, u16, slice::Items<u16>>>>
Now, you can write:

   fn did_too_many_chains() -> impl Iterator 
and be done with it.
Re:Zero-cost futures in Rust by fnord123 on Friday August 12, 2016 @05:45AM (#12274322)
That's awesome. I've been banging my head against the wall trying to implement a Drain iterator for a tree structure and it tries to recurse. I ended up with things like your Chain<Map...>> and was banging my head against the wall over it.
Re:Zero-cost futures in Rust by steveklabnik on Thursday August 11, 2016 @12:23PM (#12269530)
That is new syntax which has been accepted as a language change, but has not actually landed in the compiler yet. It was shown this way because it's much easier to understand, but is the same thing as it would be in the real code.

https://github.com/rust-lang/rfcs/blob/master/text/1522-cons... is an explanation of the feature in detail, but it boils down to "I will return you some kind of thing that implements the Future trait, but I'm not telling you exactly what it is."

Re:Zero-cost futures in Rust by michaelsbradley on Thursday August 11, 2016 @12:59PM (#12269861)
Does the need for this arise, in some sense, because Rust doesn't offer classical inheritance and so no mechanism to indicate co/contra variance?
Re:Zero-cost futures in Rust by Gankro on Thursday August 11, 2016 @01:24PM (#12270073)
Rust does let you communicate variance (it's very important for lifetimes). Variance is determined based on a type's composition, and PhantomData can be used to specify variance that doesn't immediately follow by providing an "example" of what it should behave like. The only limitation of this approach is that you can't override the compiler's reasoning -- if it sees evidence for covariance, and you provide more evidence for contravariance it will just give up and pick invariance (which is of course strictly correct if both pieces of evidence are accurate). Also I guess you can't have bivariance but like... bivariance is so bad.

All of Rust's standard collection and smaht pointer types are written in pure Rust and have relatively complex variance behaviour.

Re:Zero-cost futures in Rust by andrewflnr on Friday August 12, 2016 @12:11AM (#12273354)
For anyone else wondering what the deal is with "smaht pointers": https://www.reddit.com/r/rust/comments/3404ml/prepooping_you... I think I first saw the term on that thread but didn't stay long enough to see the explanation. It was driving me nuts! :)
Re:Zero-cost futures in Rust by aturon on Thursday August 11, 2016 @01:01PM (#12269886)
No, it's more related to the fact that Rust allows you to use traits in both a statically- and dynamically-dispatched way. You can read some more on the topic here: http://aturon.github.io/blog/2015/09/28/impl-trait/
Re:Zero-cost futures in Rust by dbaupp on Thursday August 11, 2016 @01:51PM (#12270309)
I'm not sure variance is at all related to this particular feature (maybe you could expand on your thinking?), but I also don't think the classical inheritance techniques for solving this problem are appropriate. I assume you're referring to, say, having an Iterator or Future base class and returning that. This unfortunately imposes costs like virtual calls and allocations, and is in fact already possible in Rust via trait objects (a function can return Box<Iterator<...>>, which is an allocation containing the data along with a vtable of virtual function pointers to manipulate that data). The impl trait feature is designed to allow returning closures and other unnameable/complicated types with no unnecessary cost, it's the same as returning a struct in C without compromising on programmer niceties.
Re:Zero-cost futures in Rust by michaelsbradley on Thursday August 11, 2016 @03:13PM (#12270873)
I probably misunderstood something, and it's been awhile since I looked closely at the language. Your reply, and the others to my comment, have been enlightening, thanks!
Re:Zero-cost futures in Rust by pcwalton on Thursday August 11, 2016 @12:23PM (#12269532)
It's a new feature (hopefully to be merged this week). Here are the gory details:

https://github.com/rust-lang/rfcs/blob/master/text/1522-cons...

Re:Zero-cost futures in Rust by kibwen on Thursday August 11, 2016 @12:26PM (#12269569)
Worth noting that by "hopefully to be merged this week" we mean that this initial prototype implementation will land in nightly: https://github.com/rust-lang/rust/pull/35091 . It's not being stabilized this week.
Re:Zero-cost futures in Rust by mpartel on Thursday August 11, 2016 @12:58PM (#12269852)
On a related note: why `Future<Item = Row>` instead of `Future<Row>`?

I notice it's consistent with the standard `Iterator<Item = ...>`, but why is Iterator like that?

Re:Zero-cost futures in Rust by masklinn on Thursday August 11, 2016 @01:15PM (#12270003)
Because it's an associated type (https://doc.rust-lang.org/book/associated-types.html), not a regular generic parameter.
Re:Zero-cost futures in Rust by eximius on Thursday August 11, 2016 @04:12PM (#12271267)
Aren't generic types with one associated type isomorphic to 'normal' generic types? In that sense, it is still odd.
Re:Zero-cost futures in Rust by Manishearth on Thursday August 11, 2016 @09:35PM (#12272940)
Not exactly. A given type can have infinite implementations of iterator<A> (by implementing for different A) but only one impl for iterator<Item=Foo>
Re:Zero-cost futures in Rust by eximius on Thursday August 11, 2016 @10:17PM (#12273052)
I don't think I get that without an example. Do you know of any simple examples?
Re:Zero-cost futures in Rust by Gankro on Thursday August 11, 2016 @11:32PM (#12273246)
Generic traits can be implemented multiple times, as long as the params differ. This is generally used to establish relationships between types, which can naturally be M:N. Comparison and conversion are standard use cases:

    impl PartialOrd<OsString> for OsString
    impl PartialOrd<str> for OsString
    impl PartialOrd<OsStr> for OsString
    impl PartialOrd<Cow<OsStr>> for OsString
Traits without generics can only be implemented once (because there's no generic params to "differentiate" impls). But it's often still desirable/necessary to relate types to the implementation. This is much more common, as it's generally a case that some type is a ThingDoer in only one way (and having it be a ThingDoer in more than one way may be confusing).

Associated types are often more convenient and give the compiler more room to reason about your code.

Re:Zero-cost futures in Rust by Manishearth on Friday August 12, 2016 @12:00AM (#12273323)
Gankro gave an example, but here's a better way to think about associated types:

Think about them the same way you think of a method. Methods are "associated functions". If you implement a trait on a type, it can only have one version of a trait method, not two. Similarly, it can have one associated type, not multiple.

With a generic trait the trait itself is generic; there are multiple "versions" of this trait so you can implement it multiple times for different parameters and different methods.

A concrete way to think about this is overloaded methods. Rust doesn't have the regular kind of overloading, but it does have the Fn traits.

https://doc.rust-lang.org/core/ops/trait.FnOnce.html

(ignore the rust-call stuff)

The trait is essentially:

    trait FnOnce<Args> {
        type Output;
        fn call_once(self, args: Args);
    }
Note that it has both a type parameter and an associated type.

Now, we might have a type Foo which is `FnOnce<(u8,),Output=bool>`. This means that it takes a single integer in, and outputs a bool. We can overload it by also implementing `FnOnce<(char,), Output=bool>` and `FnOnce<(u8,u8),Output=char>`. This means that it can also take in a char and return a bool, or take two integers and output a char.

Now, given these impls, I can try to overload it with `FnOnce<(u8,u8),Output=bool>`. However, we can't. Because this means that the function will return either a book or a char when you feed it two ints. This is not how we want functions to behave, and this is why Output is an associated type. For a given trait implemented on a given object (in this case FnOnce<(u8,u8)> implemented on Foo), there is only one output type. But there can be multiple versions of the trait implemented by changing the generic part. So, while an overloaded function may take in multiple different input types, each set of inputs has only one possible output type.

Of course, Rust doesn't forbid having it the other way around -- we could make Output a generic parameter too, and have overloaded functions which can have different output types for the same input (and need type annotations to choose). But we don't want FnOnce to work that way, so we don't have it like that.

Similarly, given an iterator, there is only one type it can produce. We don't want there to be types which are iterators over multiple things.

On the other hand, the trait PartialEq has a single type parameter. This is because we want you to be able to test equality between many types.

Re:Zero-cost futures in Rust by dan00 on Friday August 12, 2016 @05:42AM (#12274311)
> Of course, Rust doesn't forbid having it the other way around -- we could make Output a generic parameter too, and have overloaded functions which can have different output types for the same input (and need type annotations to choose). But we don't want FnOnce to work that way, so we don't have it like that.

Can you please explain why FnOnce shouldn't work this way?

Re:Zero-cost futures in Rust by Manishearth on Friday August 12, 2016 @10:58AM (#12276211)
Because overloading the return type (for the same inputs) is pretty unusual :)

Rust has the machinery to deal with this -- you'd need type annotations -- it just gets annoying.

Specific functions in Rust do get "overloaded" by return type by returning a generic parameter (e.g. the collect() method on iterators), but in general functions are expected to work without needing type annotations on return.

I mean, allowing this kind of use of FnOnce isn't a bad idea. I don't see major issues with it, just minor ones. That's not the choice that was made during the design. I'm sure if we looked at the original design rfcs this point will have come up somewhere :)

I wasn't involved in that decision, so I'm not aware of the exact reasoning.

Re:Zero-cost futures in Rust by dan00 on Saturday August 13, 2016 @07:44AM (#12281113)
Thanks! I just thought their might be some quite important technical reasons for this decision.

After all overloading the return type is quite usual and extremly helpful for parsing or the conversion of types.

But these kind of operations most likely will be done with a Trait in Rust, so having this option for closures might not be that important.

Re:Zero-cost futures in Rust by Manishearth on Saturday August 13, 2016 @09:33AM (#12281420)
Exactly -- it only stops you from creating function-likes that have multiple return types (not closures, because closures aren't locally generic anyway). If you really need such behavior, regular generic functions or traits will work, e.g. `.collect()`
Re:Zero-cost futures in Rust by kibwen on Thursday August 11, 2016 @12:25PM (#12269543)
It's an upcoming feature, AFAIK the syntax is still being decided upon. What it means is that the `get_row` function returns some type that implements the `Future` trait, but we refrain from saying exactly which type that is. This is useful for various reasons, and in this case it's probably just being used because typing out the concrete type for some of these deeply-nested and combined types quickly starts to resemble C++ template hell.