5 ms·
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: i3
by losvedir 10y ago
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?
- steveklabnik 10y agoThat 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-conservative-impl-trait.md 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."
- michaelsbradley 10y agoDoes the need for this arise, in some sense, because Rust doesn't offer classical inheritance and so no mechanism to indicate co/contra variance?
- aturon 10y agoNo, 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/ http://aturon.github.io/blog/2015/09/28/impl-trait/
- Gankro 10y agoRust 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.
- andrewflnr 10y agoFor anyone else wondering what the deal is with "smaht pointers": https://www.reddit.com/r/rust/comments/3404ml/prepooping_your_pants_with_rust/cqrd0rq?context=3 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! :)
- dbaupp 10y agoI'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.
- michaelsbradley 10y agoI 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!
- pcwalton 10y agoIt'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-conservative-impl-trait.md https://github.com/rust-lang/rfcs/blob/master/text/1522-cons...
- kibwen 10y agoWorth 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 https://github.com/rust-lang/rust/pull/35091 . It's not being stabilized this week.
- kibwen 10y agoIt'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.
- aturon 10y agoThis 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/ http://aturon.github.io/blog/2015/09/28/impl-trait/ - https://github.com/rust-lang/rfcs/pull/1522 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 :)
- agmcleod 10y agoSounds awesome. Love it :)
- dukerutledge 10y agoSo, existentially quantified types?
- GolDDranks 10y agoYes. 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.
- dukerutledge 10y agoYeah, since rust is eager existential types seem like a necessary evil.
- mpartel 10y agoOn 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?
- masklinn 10y agoBecause it's an associated type (https://doc.rust-lang.org/book/associated-types.html https://doc.rust-lang.org/book/associated-types.html), not a regular generic parameter.
- eximius 10y agoAren't generic types with one associated type isomorphic to 'normal' generic types? In that sense, it is still odd.
- Manishearth 10y agoNot exactly. A given type can have infinite implementations of iterator<A> (by implementing for different A) but only one impl for iterator<Item=Foo>
- eximius 10y agoI don't think I get that without an example. Do you know of any simple examples?
- Gankro 10y agoGeneric 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.
- Ygg2 10y agoRust 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.
- fnord123 10y agoThat'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.
- deleted 10y ago[deleted]