Why such constraint?

let v = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let col: Vec<i32> = v.iter().filter(|&&x| x % 2 == 0).collect();

I'm getting an error:

value of type Vec<i32> cannot be built from std::iter::Iterator<Item=&{integer}>
help: the trait FromIterator<&{integer}> is not implemented for Vec<i32>

So, I do understand what the error is saying but why is this and issue? Why the decision was made to not allow to create Vec from iterator of references to values of that type? What was the rationale?

Because it's not possible in the general case. A vector owns its items, so unless your elements are Clone or Copy, you can't transfer the ownership of the item from behind the reference and into the vector.

If your elements are cloneable or copiable, you can call .cloned() or .copied() on the iterator before collecting, thus either explicitly acknowledging the cost (in the former case), or asserting that this cost is negligible (in the latter).

1 Like

Thanks, I knew there was some reason, now I know the reason. Makes perfect sense.

I think the real answer is: It's just conventionally the case that .collect() cannot turn an iterator of &T into a vec of T when T is Clone or Copy, and the reason for that convention is that it allows type inference on the vec type. If you have an iterator of known item type and collect into a Vec<_> then the compiler can infer the element type of the vector (to be the same type as the iterator's item type). If an iterator of e.g. &i32 could instead produce either Vec<i32> or Vec<&i32>, then this inference capability would break.

A good indication in support of that this really is the reasoning behind that convention comes by the fact that for the Extend trait, Vec<T> does support iterators of &T, at least if T: Copy.

let v = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let mut col: Vec<i32> = vec![];
col.extend(v.iter().filter(|&&x| x % 2 == 0)); // works, does not need
                                               // an extra `.copied()`

...by the way, the fact that for .extend, a type &T is still not supported when it implements Clone but not Copy reveals another conventions: don't hide expensive cloning operations. However copying (when T: Copy) is usually cheap, so the convenience makes sense here. When using .extend, the type of the vec you call the method on is usually already known, so there's no benefit of better type inference you could get by not supporting iterators of &T on a Vec<T>.

Analogous implementations of Extend, but not FromIterator for &T when T: Copy exist for maps and sets, linked-lists and vec-deque.

Strings and other string-like owned types support both .extend and .collect for iterators of &char (or of &str); and for those types there's no type inference benefits to be had in not supporting those iterators.


This is certainly true, but I think my answer still holds. It's exactly the non-genericity of String that makes it possible to have an impl FromIterator<Item=&char>, because it's known that char: Copy, so there's an opportunity to have the convenience impl without hiding expensive clones.

I actually find it beautiful about Rust - not hiding expensive cloning, being transparent and explicit.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.