4 ms·
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 li
by mpartel 10y ago
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?
- 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.
- Manishearth 10y agoGankro 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 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.
- dan00 10y ago> 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?
- deleted 10y ago[deleted]