The challenge is getting the type system semantics for this correct; you need some set of type system rules (probably intermediated by traits) such that the compiler can desugar this the way you want it to.
For example, nums.iter().filter(…).copied().collect() is a nicer equivalent of:
Iterator::collect(Iterator::copied(Iterator::filter(
<[u32]>::iter(<Vec<u32> as Deref>::deref(&nums)),
|&n| n % 2 == 0,
)));
To make this a useful proposal, you'd need to explain what the rules the compiler follows are to get from nums.filter(…) to Iterator::filter(<[u32]>::iter(<Vec<u32> as Deref>::deref(&nums)), …) - for current Rust, this is a combination of autoref and method call search that can be translated to a single unambiguous set of function calls by the compiler.
The reason why filter is implemented on the Iterator trait instead of the collection itself is that its implementation can be abtracted over any iterator.
By doing so, we don't have to implement filter on all existing collection types.
Also, generaly collections have three methods to create an iterator into_iter, iter and iter_mut which explicitly state if we want to take the ownership of the values or borrow them mutably or not.
The trouble with the somevec.filter(..) syntax is that it's ambiguous whether we should insert .iter(), .iter_mut() or .into_iter() implictly. Maybe one could look at what the closure accepts, so that
If you want to prototype this, you could write a crate with an extension trait and publish it to crates.io. People would just need to use your trait and then they would have a filter method on Vec that work like this.
What could, perhaps, make more sense would be to define filter and map directly on the collection, producing a collection of the same type. I.e. drop both the .iter() and .collect() calls. If you don't need the flexibility and explicitness of .into_iter(), .iter() and .iter_mut(), you probably don't need the full flexibility and laziness of iterators in the first place.
In that case the signatures of methods are easy to write:
pub trait Filter
where
Self: Sized + IntoIterator + FromIterator<<Self as IntoIterator>::Item>,
{
fn filter(self, f: impl FnMut(&Self::Item) -> bool) -> Self {
self.into_iter().filter(f).collect()
}
}
impl<T> Filter for T
where
T: Sized + IntoIterator + FromIterator<<T as IntoIterator>::Item>,
{}
pub trait Map<R>
where
Self: Sized + IntoIterator,
{
type Output: FromIterator<R>;
fn map(self, f: impl FnMut(Self::Item) -> R) -> Self::Output {
self.into_iter().map(f).collect()
}
}
impl<T, R> Map<R> for Vec<T> {
type Output = Vec<R>;
}
impl<K, V, R> Map<(K, R)> for BTreeMap<K, V>
where
K: Ord + Eq,
{
type Output = BTreeMap<K, R>;
}
This can be useful when the API shape requires passing around vectors and maps, rather than iterators. Of course, this isn't particularly rusty, so I don't expect such extension traits to get into stdlib.
Many people would take that as an invitation to slap a series of map and filter calls on a collection, causing a bunch of intermediary collections to be created. Considered perfectly idiomatic, for example, in Scala. If this does not perform well, many conclude they need lazy collections.
.iter() makes clear both the borrowing behavior and the fact that the operation is lazy and doesn't allocate (until collected). Removing it would not only not be a convenience but would be actively harmful by obscuring important details about the nature of the operation.