1

Why does this code compile:

pub fn main() {
    let my_vec = vec!["b", "c", "d"];

    let res = ["a"].iter()
        .chain(my_vec.iter().map(|s| s))
        .collect::<Vec<_>>();

    println!("{:?}", res)
}

And this doesn't:

pub fn main() {
    let my_vec = vec![(1, "b"), (2, "c"), (3, "d")];

    let res = ["a"].iter()
        .chain(my_vec.iter().map(|s| s.1))
        .collect::<Vec<_>>();

    println!("{:?}", res)
}

Is there a way to make it compile?

Background

I am trying to refactor my code that was constructing a vector by appending elements to it. It looked something like this:

pub fn main() {
    let my_vec = vec![(1, "b"), (2, "c"), (3, "d")];

    let mut res = Vec::new();
    res.push("a");
    for (_, s) in &my_vec {
        res.push(s);
    }

    println!("{:?}", res)
}

I changed it into something that will collect this vector from a chain of iterators without modifying a mutable instance in place:

pub fn main() {
    let my_vec = vec![(1, "b"), (2, "c"), (3, "d")];

    let res = ["a"].iter()
        .chain(my_vec.iter().map(|s| s.1))
        .collect::<Vec<_>>();

    println!("{:?}", res)
}

The construction I came up with doesn't compile:

error[E0271]: type mismatch resolving `<[closure@iter.rs:5:34: 5:41] as std::ops::FnOnce<(&({integer}, &str),)>>::Output == &&str`
 --> iter.rs:5:10
  |
5 |         .chain(my_vec.iter().map(|s| s.1))
  |          ^^^^^ expected `str`, found `&str`
  |
  = note: expected reference `&str`
             found reference `&&str`
  = note: required because of the requirements on the impl of `std::iter::Iterator` for `std::iter::Map<std::slice::Iter<'_, ({integer}, &str)>, [closure@iter.rs:5:34: 5:41]>`

error[E0599]: no method named `collect` found for struct `std::iter::Chain<std::slice::Iter<'_, &str>, std::iter::Map<std::slice::Iter<'_, ({integer}, &str)>, [closure@iter.rs:5:34: 5:41]>>` in the current scope
   --> iter.rs:6:10
    |
6   |           .collect::<Vec<_>>();
    |            ^^^^^^^ method not found in `std::iter::Chain<std::slice::Iter<'_, &str>, std::iter::Map<std::slice::Iter<'_, ({integer}, &str)>, [closure@iter.rs:5:34: 5:41]>>`
    |
    = note: the method `collect` exists but the following trait bounds were not satisfied:
            `<std::iter::Map<std::slice::Iter<'_, ({integer}, &str)>, [closure@iter.rs:5:34: 5:41]> as std::iter::Iterator>::Item = &&str`
            which is required by `std::iter::Chain<std::slice::Iter<'_, &str>, std::iter::Map<std::slice::Iter<'_, ({integer}, &str)>, [closure@iter.rs:5:34: 5:41]>>: std::iter::Iterator`
            `std::iter::Chain<std::slice::Iter<'_, &str>, std::iter::Map<std::slice::Iter<'_, ({integer}, &str)>, [closure@iter.rs:5:34: 5:41]>>: std::iter::Iterator`
            which is required by `&mut std::iter::Chain<std::slice::Iter<'_, &str>, std::iter::Map<std::slice::Iter<'_, ({integer}, &str)>, [closure@iter.rs:5:34: 5:41]>>: std::iter::Iterator`

I tried adding and removing & signs here and there but nothing helped. I also tried a simpler variant:

pub fn main() {
    let my_vec = vec!["b", "c", "d"];

    let res = ["a"].iter()
        .chain(my_vec.iter().map(|s| s))
        .collect::<Vec<_>>();

    println!("{:?}", res)
}

This doesn't seem fundamentally different to me, but the compiler doesn't have a problem with it.

In my original problem, the result isn't a Vec<&str> but a Vec<&T> where T is some other struct, but I simplified the example, and the error messages are pretty much identical.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
gmoshkin
  • 1,106
  • 8
  • 22
  • These questions are different but the answer is common, it is about the behavior of field access expression, please check: https://stackoverflow.com/questions/53692702/why-is-a-reference-variable-accessed-via-auto-deref-moved – Ömer Erden Aug 25 '20 at 18:02
  • 1
    `s.1` implicitly derefences `s` to be able to access the second element of the pair. You should try `&s.1` instead. – Sven Marnach Aug 25 '20 at 18:02
  • It looks like your question might be answered by the answers of [Iterating over a slice's values instead of references in Rust?](https://stackoverflow.com/q/40613725/155423). If not, please **[edit]** your question to explain the differences. Otherwise, we can mark this question as already answered. – Shepmaster Aug 25 '20 at 18:03
  • See also [What is the difference between iter and into_iter?](https://stackoverflow.com/q/34733811/155423) – Shepmaster Aug 25 '20 at 18:04
  • 1
    I'd use `...iter().copied().chain...` – Shepmaster Aug 25 '20 at 18:04
  • @Shepmaster thanks a ton, you've solved it! – gmoshkin Aug 25 '20 at 18:23
  • 1
    In this particular case I'd probably use `std::iter::once("a")` instead of `["a"].iter().copied()`, but if you have more than one string in the `[...]` then `once` doesn't apply. – trent Aug 25 '20 at 19:12

0 Answers0