Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

a collection of type `str` cannot be built from `std::iter::Iterator<Item=char>`

Tags:

rust

The following code fails to compile

fn main() {
    let n = 10;
    let mut s = String::new();
    let v = vec!['a'; n];
    s += &v.into_iter().collect();
}

with the error:

error[E0277]: a collection of type `str` cannot be built from an iterator over elements of type `char`
 --> src/main.rs:5:25
  |
5 |     s += &v.into_iter().collect();
  |                         ^^^^^^^ a collection of type `str` cannot be built from `std::iter::Iterator<Item=char>`
  |
  = help: the trait `std::iter::FromIterator<char>` is not implemented for `str`

However this modification seems to work:

fn main() {
    let n = 10;
    let mut s = String::new();
    let v = vec!['a'; n];
    let t: String = v.into_iter().collect();
    s += &t;
}

I am trying to understand the reason. Any hints/explanation would help. Thanks.

like image 638
RSS Avatar asked Jan 04 '20 11:01

RSS


1 Answers

Firstly, what works:

fn main() {
    let n = 10;
    let mut s = String::new();
    let v = vec!['a'; n];
    s += &v.into_iter().collect::<String>();
}

Secondly, let's explore why that works. You can do add-assign += on String because it implements AddAssign trait:

impl<'_> AddAssign<&'_ str> for String

but only if the right-hand-side is a &str. So in your first example, rustc tries to collect a vector of char into str. It won't work because the signature of Iterator::collect:

fn collect<B>(self) -> B
where
    B: FromIterator<Self::Item>,

dictates that an iterator can only be collected into a container which implements FromIterator. Among all types that do implement it, you can see str is not one of them, but String is. That's the error message you've witnessed.

On the other hand, in your second example and the code in this answer, rustc is specifically told to collect into a String. And since String implements Deref trait with str as the target type:

impl Deref for String {
    type target = str;
}

a &String can be coerced to &str. The compiler is now happy.


So now you should be able to decipher why the following funny looking code also works:

fn main() {
    use std::borrow::Cow;

    let n = 10;
    let mut s = String::new();
    let v = vec!['a'; n];
    s += &v.into_iter().collect::<Cow<'_, str>>();
}
like image 147
edwardw Avatar answered Sep 27 '22 22:09

edwardw