Open canndrew opened 6 years ago
My only thought on the matter:
However Vec
is, in some sense, the canonical type that satisfies these requirements - when iterated it gives back the exact same items that where .extend-ed into it, in the exact same order.
This property is far from unique. Even in std
there's VecDeque
(which could be disregarded as being more complex and hence less canonical) and LinkedList
(which can't be dismissed on the same grounds). I could still see an argument for Vec being the default sequence type (and in practice, I don't think many people would dispute that), but one that is less nice and clean-cut than just pointing at this property.
If you're in no_std
land, then ArrayVec<?>
sounds more natural. Right now, the ?
remains ambiguous, but maybe not if we get VLAs and know the size. And ArrayVec
might eventually become superfluous, giving just a VLA.
I wish this were a property of the functions rather than the known trait bounds. I feel like a default of Vec<_>
should somehow be tied to Iterator::{collect, unzip, partition}
.
Type parameter defaults for functions were accidentally supported in the grammar for some time and I'm sure I've written them a number of times expecting them to do something. I gather from that issue that it is not necessarily clear how these should behave, but without being able to find much discussion on it I'm not sure I see how they are any muddier than defaults for parameters in types and traits.
I guess the difference for structs and traits is that those defaults are applied each and every time we write out the type, regardless of context. Whereas in the case of functions, we're moreso hoping for the default to merely act as a "suggestion" to type inference.
@ExpHP See #2321
One place this could be useful is in automatic-enuming of return types in futures. For example: say I have
.and_then(|x| {
match x {
true => some_future,
false => some_other_future,
}
})
This won't compile because some_future
and some_other_future
have different types. Instead we have to box them:
.and_then(|x| {
match x {
true => Box::new(some_future) as Box<Future<...>>,
false => Box::new(some_other_future) as Box<Future<...>>,
}
})
However this does an unnecessary allocation. What we really want is to create an anonymous 2-variant enum that implements Future
. Using this RFC, and the anonymous enums RFC, and the generic-sized tuples RFC (extended to anonymous enums), we could make the default type for Future<Item=T, Error=E> + From<A0> + From<A1> + ... + From<An> where A*: Future<Item=T, Error=E>
be an anonymous enum over A0 .. An
which implements Future<Item = T, Error=E>
.
If you're already doing a Box<Future>
then an Either
as impl Future
works just as well, right? I suppose Either<impl Future,impl Future>
even works.
@burdges I guess so, yeah Would still be nice to have generic-arity anonymous enums for this though.
We could maybe unify the various Either
types so the same Either
type worked for numerous traits, including Iterator
, Future
, Fn*
, Clone
, *Hash*
, some of Deref*
, Borrow*
, As*
, and some important traits from elsewhere like Rng
. If this exists, then one might build an anonify!(..)
proc macro to automatically build Either
trees.
Some types are only visible through the traits that they implement. Currently, when Rust cannot infer a type it will either raise an error or, in some unclearly-defined situations, default the type to
!
(or()
). Perhaps we could allow defaulting in more situations and allow the defaulting to be trait-directed.For example, the signature of
Iterator::unzip
is:Now suppose I write:
If I haven't touched
foo
anywhere else my code, then this won't compile. All rust knows aboutfoo
's type is that it must implDefault + Extend<X> + IntoIterator<Item=X>
, and there may be lots of types that satisfy this requirement. HoweverVec<X>
is, in some sense, the canonical type that satisfies these requirements - when iterated it gives back the exact same items that where.extend
-ed into it, in the exact same order. Perhaps the compiler should be able to inferVec<X>
in this case. Perhaps the standard library should be able to specifyVec<X>
as the inferred type in cases like this with a declaration like:Multiple
default
declarations could override each other by being more specific in much the same way that impl specialization works. When the compiler tries to default a type which satisfies some required boundsB
, it looks for the most specific traitA <: B
for which there is a default type, and uses that type if it satisfiesB
.The current behaviour of defaulting to
!
could be replaced by a declaration inlibcore
of:There could also be default of (just for example):
So, is this a good idea? Or is it terrible?