Why Vec's API isn't bigger than it's iter?

Even though IDEs can autocomplete function calls, I find myself calling .iter() on Vec almost every single time. Why is that?

I know Vec has powerful APIs like extend(), but in fact I often need foreach(), filter(), and similar methods — all of which live under .iter(). This actually caused me some trouble: I didn't realize
vec.iter().max() already existed, so I went and opened a topic on Rust Internals asking for creating function max() on collections, lol.

Why not implement these methods directly on Vec? It would make things much more discoverable. By the way, I don't think using Deref is a great solution here either, since iter_mut() also needs to be considered.

I've only been using Rust for a few months, so I'm still a noob. I didn't even know iter_mut() existed because I've just never needed it in my own development so far.

Adding those methods to Vec in addition to Iterator would violate the TOOWTDI rule of API design:

There should be one-- and preferably only one --obvious way to do it.

This is also called Orthogonality.

If you create two ways of doing the same thing, you've complicated the API, made learning the API harder, made documentation longer and split the user ecosystem into two factions.

Because the Iterator interface is vastly more generally useful than just with Vec, you should put effort into learning it, or at least learning to always check if it might contain some useful tool when you're trying to do anything that involves iterating over something (whether it's arrays, slices, vectors, sets, maps, …)

As for IDE discoverability, at least RustRover offers autocompletions like v.filtv.iter().filter().

But at the same time, there are vec.len() and vec.iter().len() already. They does exactly the same thing, and I think there's no reason to use the second one.

True, and that is a violation of the TOOWTDI principle. But that doesn't mean more violations would be an improvement. I guess vec.len() is so common that the extra method in the API is a reasonable trade-off, whereas things like foreach are less common so expanding the API isn't worth the cost. Note that you'd need the equivalent of vec.into_iter().foreach(), vec.iter().foreach() and vec.iter_mut().foreach(), so 3 extra methods in addition to one Iterator method.

Iterators in general don't have len(), they only have count() which is O(n), consumes the iterator, and may in fact never return if the iterator is infinite. Vec's iterators are ExactSizeIterators because they do know their exact length. Sometimes it can be useful to accept an ExactSizeIterator in some generic API (or something that's IntoIterator<IntoIter: ExactSizeIterator>) because it's more general than taking a slice (although I believe it's more commonly used with specialization to optimize certain implementations within the standard library). So that's more of a case of being part of an abstraction hierarchy than violating orthogonality.

In the specific case of len(), I'd argue that the length of a slice/Vec is a fundamental property of it in a way that doesn't apply for something like max(). The most natural way to implement vec.len() is to directly read the corresponding field of the Vec, while the only reasonable way I can think of to implement a vec.max() is to use its iterator. Conceptually, I'd say that a hypothetical vec.max() is "derived" from vec.iter().max(), while it's the other way around for vec.len() and vec.iter().len() -- and thus, you only need vec.iter().max() and vec.len() as "primitives".

The role of vec.iter().len() is a bit more niche: you generally wouldn't use it directly, but it comes into play when a function takes an ExactSizeIterator, which can be created from a Vec or a HashMap or a VecDeque or .... It's mainly useful in the context of generic functions. (I suppose you could also use the len() of vec.iter() to check how many elements are remaining if you're using the iterator manually.)

That's true, sometimes we may use .collect() and .count() to check the result of operations:

vec.iter().filter(|&x| x % 2 == 0)..count() 
// check how many even numbers are in the Vec
vec.iter().map(|x| fn_1(x)).collect()
// map all the elements in Vec by fn_1()

In my pov , I just hate using .iter() again and again, it makes my code too long. Now I realize it may cause serious problems if impl the methods directly to Vec. It's annoying when finish slicing, I need to call .iter() again for more operations.

*num[..num.len()/2].iter().max().unwrap()

.into_iter() and .iter_mut() can do the same thing like .iter(), but .into_iter() will take its ownership and iter_mut() can't even be called because in most cases functions' arguments are immutable. Therefore, three extra methods in addition to one is true but using the other methods are wired.

.iter().len() is a wired usage, too.

vec.iter().len() 
// === vec.len()
vec[..vec.len()/2].iter().len() 
// It's wired because when you slice a Vec or other thing, 
// you actually know the length of the slice when slicing.
// Moreover, vec[..vec.len()/2].len() is available in this case.
vec.iter().filter(|&x| x % 2 == 0).count() 
// You cannot call .len() in this cases because there's 
// no way to get the exact count in O(1). We must use
// the .count() instead(O(n))

Things like IntoIterator<IntoIter: ExactSizeIterator> may be found in some libs. It just takes over what Vec do.

Wait until you hear about into() and unwrap() :face_savoring_food:

Kidding aside, this is basically just the style of the Rust API, being very explicit about not just what happens but when. It is often annoying, but it does start to vanish after a few years in my experience, and it does at least have benefits when you start getting into the metaprogramming stuff.

It's something in theory that could be addressed by some funky language feature (perhaps a pipeline operator, for example), but you're very likely to end up just exchanging one headache for another.

One argument for having the clear inherent vs trait method split is you don't end up with the common mistake of thinking you need a Vec when you're actually only using slice methods (or a String instead of &str)

There are also question of cost. While vec.iter().len() may be optimized to perform in O(1) it's not obvious, from reading the documentation, if it's actually done or not, while vec.len() is “obviously” O(1).

Most other operations mentioned need to process O(N) items, whether you use them with iterator or directly with vector and it doesn't make much sense to implement them directly on vector, but when expected complexity is different I prefer version that is “obviously correct” to an attempt to to remove everything possible.