4

I'm confused about this:

use itertools::Itertools; // 0.10.0

fn main() {
    let combos = ["a", "b", "c"].iter().combinations(2).collect::<Vec<_>>();
    println!("{:#?}", combos[0].join(""));
}
error[E0599]: the method `join` exists for struct `Vec<&&str>`, but its trait bounds were not satisfied
   --> src/main.rs:5:33
    |
5   |       println!("{:#?}", combos[0].join(""));
    |                                   ^^^^ method cannot be called on `Vec<&&str>` due to unsatisfied trait bounds
    |
    = note: the following trait bounds were not satisfied:
            `Vec<&&str>: Iterator`
            which is required by `Vec<&&str>: Itertools`
            `<[&&str] as Join<_>>::Output = _`
            `[&&str]: Iterator`
            which is required by `[&&str]: Itertools`
  1. Why is combos[0] a Vec<&&str>, rather than a Vec<&str>?
  2. What are the unsatisfied trait bounds here?
  3. How do I resolve the error?

I'm sure these answers are derivable from the book, but I was hoping not to have to read the whole book to write this trivial program. Perhaps one can not merely dabble in Rust ...

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Sasgorilla
  • 2,403
  • 2
  • 29
  • 56
  • I'm not sure about the real goal. Are you looking for `combos[0].iter().join("")` ? – Denys Séguret Apr 28 '21 at 16:22
  • 3
    You get a `Vec<&&str>` because you're iterating over elements that are _in the container_. This requires the iterator to yield _references_ to such elements. And since your container contains `&str`, a reference to it is `&&str`. Since references are trivially copiable, you can fix that by adding `.map(|x| *x)` or `.copied()` (which is a nicer way of saying the same thing) after `.iter()`. With that change, your program compiles and outputs `"ab"`. Happy dabbling! – user4815162342 Apr 28 '21 at 16:23
  • 3
    Please try to stick to [one question per question](https://meta.stackoverflow.com/q/275908/3650362). Question 1 is a duplicate of [What is the difference between iter and into_iter?](https://stackoverflow.com/q/34733811/3650362) (`iter()` gives references, and the array contains references already, so `&&str`). – trent Apr 28 '21 at 16:28
  • 1
    `["a", "b", "c"].iter().copied()...` – Shepmaster Apr 28 '21 at 16:29
  • Your question might be answered by the answers of [Join iterator of &str](https://stackoverflow.com/q/56033289/155423) / [What's an idiomatic way to print an iterator separated by spaces in Rust?](https://stackoverflow.com/q/36941851/155423). If not, please **[edit]** your question to explain the differences. Otherwise, we can mark this question as already answered. – Shepmaster Apr 28 '21 at 16:31
  • @Shepmaster It would be interesting to address the missing trait bounds as reported by the compiler. I'm not a beginner, and I can't say I fully understand that error message, and the other answers don't seem to cover it either. – user4815162342 Apr 28 '21 at 16:46
  • @user4815162342 I could make an answer, but if I draw your attention to `Vec<&&str>: ` **`Itertools`** does that sufficiently explain it to someone with your knowledge level? – Shepmaster Apr 28 '21 at 17:00
  • @Shepmaster (I'm intentionally refraining from looking at your answer while writing this comment.) I actually noticed that, and assumed that `use itertools::Itertools` is pulling the `join()` from itertools, which does requires an `Iterator`. But that wouldn't make sense when `Vec` is clearly not `Iterator` - I'd expect the compiler to pick the `join` method belonging to `Vec` (which also fails to work with `&&str`, but with an error message presumably not mentioning `Itertools`). I'm now off to read the answer. – user4815162342 Apr 28 '21 at 17:31
  • @Shepmaster Ok, the last paragraph of your answer confirms that I wasn't completely off the mark and that a certain amount of confusion is introduced by the error. What I failed to appreciate was that `join()` is not a method on `Vec`, but "inherited" from slice instead. So the compiler has to choose between two failing interpretations, and chooses one, which makes the error confusing in this particular case because the user actually intended to call the other. Sadly, diagnostic failures like this can make Rust can seem much more intimidating to beginners than is necessary. – user4815162342 Apr 28 '21 at 17:36
  • 1
    @user4815162342 I do have some feelers out there to see if I can get a more satisfactory "this is chosen because of X, Y, Z" answer. – Shepmaster Apr 28 '21 at 17:40
  • @Shepmaster - I'm assuming your (excellent) answer below covers it, but feel free to mark as duplicate (or edit for clarity) if you still think that's warranted. – Sasgorilla Apr 28 '21 at 17:56

1 Answers1

4

TL;DR the fix:

["a", "b", "c"].iter().copied().combinations(2).collect::<Vec<_>>()

Parts of your question have existing answers:

Concentrating on the remaining piece, we look at the error message:

= note: the following trait bounds were not satisfied:
        `Vec<&&str>: Iterator`
        which is required by `Vec<&&str>: Itertools`
        `<[&&str] as Join<_>>::Output = _`
        `[&&str]: Iterator`
        which is required by `[&&str]: Itertools`

You've created a Vec<&&str>. You are presumably trying to call slice::join:

pub fn join<Separator>(
    &self,
    sep: Separator
) -> <[T] as Join<Separator>>::Output
where
    [T]: Join<Separator>, 

The trait bounds for that method are not met:

fn satisfies<T: std::borrow::Borrow<str>>() {}
satisfies::<&&str>();
error[E0277]: the trait bound `&&str: Borrow<str>` is not satisfied
 --> src/main.rs:9:5
  |
8 |     fn satisfies<T: std::borrow::Borrow<str>>() {}
  |                     ------------------------ required by this bound in `satisfies`
9 |     satisfies::<&&str>();
  |     ^^^^^^^^^^^^^^^^^^ the trait `Borrow<str>` is not implemented for `&&str`

The compiler has instead selected Itertools::join:

pub fn join(&mut self, sep: &str) -> String
where
    Self::Item: Display, 

This also fails to resolve, as neither Vec<&&str> nor [&&str] implement Iterator, which would be required for Itertools to be implemented for those types.

I don't know why the compiler chooses to only report one of the failures. Perhaps it considers one more likely to be what you intended, or one is closer to being implemented, or maybe it's a bug or oversight in the trait resolution algorithm.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • I'm confused by notation in the method signature of `slice::join` above. It seems to indicate a *return type* of `[T] as >`. But given that [`Join`](https://doc.rust-lang.org/std/slice/trait.Join.html) is the trait specifying the `join` method, isn't `[T] as >` the type of `self`, not (necessarily) the type of the return value? – Sasgorilla Apr 28 '21 at 18:08
  • 1
    @Sasgorilla see [table b-3](https://doc.rust-lang.org/1.51.0/book/appendix-02-operators.html). The return type is `<[T] as >>::Output` — the associated type `Output` of the trait `Join` when applied to the type `[T]`. – Shepmaster Apr 28 '21 at 18:13