Extend expecting reference for my Iterator

I have a struct that wraps a Vec<Vec<i32>>, and associated trait for that struct. I'm trying to implement an Iterator for the struct such that I can call extend on the wrapped Vec using the iterator.

Simplified version of the code.

I get the following error:

34 |         self.rows.extend(table.iter());
   |                   ^^^^^^ expected reference, found struct `std::vec::Vec`
   |
   = note: expected type `&std::vec::Vec<i32>`
              found type `std::vec::Vec<i32>`

I'm pretty confused by this because my Iterator is returning Option<&Vec<i32>>. So why is extend complaining about finding Vec<i32>? Thanks!

The inference is going the opposite way. See the part of the error that's been left out:

type mismatch resolving `<RowIter<'_> as std::iter::IntoIterator>::Item == std::vec::Vec<i32>`

self.rows.extend wants some Vecs. You gave it some &Vecs.

.iter() allows looking at objects, but doesn't allow keeping them. You can't keep borrowed objects - that's stealing! You need .iter().cloned() to get your own copies, or take ownership of table instead.

@svr & @kornel, thank you very much for your respective help! I guess I was confused by the error message from the compiler, because usually when I see something like:

   = note: expected type `&std::vec::Vec<i32>`
              found type `std::vec::Vec<i32>`

it means I passed the wrong parameter... so that's why I kept thinking I needed a &std::vec::Vec<i32>, even though I was pretty sure I was creating that.

Any reason why the compiler would generate that message instead of:

   = note: expected type `std::vec::Vec<i32>`
              found type `&std::vec::Vec<i32>`

For anyone else who stumbles across this, the full fix was 2 parts: removing the borrow on table and using into_iter() instead of iter().

    fn append(&mut self, table :impl Table) {
        self.rows.extend(table.into_iter());
    }

It's a matter of perspective. If you assume the argument has the correct type, then the method is wrong. If you assume the method has the correct type, then the argument is wrong. The order of expected vs found may change depending on what compiler checks first.

I get that, but it seems inconsistent, consider:

fn foo(a :&String) {
    println!("{}", a);
}

fn main() {
    let a = String::from("hello world");
    
    foo(a);
}

You get:

8 |     foo(a);
  |         ^
  |         |
  |         expected reference, found struct `std::string::String`
  |         help: consider borrowing here: `&a`
  |
  = note: expected type `&std::string::String`
             found type `std::string::String`

So how do you know which one to trust? Or rather, which type to change to make them the same? I guess this is compounded by the fact that types are implicit.

It's a side-effect of how type inference works: it fills in the blanks as it learns about types until everything is known, of there's a conflict. But which way you get a conflict depends on which parts of the equation you've speculatively chosen as correct first.

Because extend is generic and takes IntoIterator the type inference has to dig through layers of indirection before it notices something's wrong:

error[E0271]: type mismatch resolving `<std::slice::Iter<'_, std::string::String> as std::iter::IntoIterator>::Item == std::string::String`

I'm not sure if it's fixable. For now the solution is to look at the types yourself to figure out which is which. .iter() == & is one of the things that you need to know.

@kornel, I see what you're saying, but I still think it'd be more readable/understandable if the two (expected & found) were flipped in that instance. I do see the ^^^ are pointing to different things (method vs parameter), so that's an indication as well.

That said, I think you'll agree with me that this one is pretty poor:

error: `impl` item signature doesn't match `trait` item signature
  --> src/row_table.rs:52:5
   |
52 |     fn group_by(&self, column: &str) -> Result<HashMap<&Value, RowTableSlice>, TableError> {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found fn(&row_table::RowTableSlice<'_>, &str) -> std::result::Result<std::collections::HashMap<&value::Value, row_table::RowTableSlice<'_>>, TableError>
   | 
  ::: src/lib.rs:30:5
   |
30 |     fn group_by(&self, column :&str) -> Result<HashMap<&Value, T>, TableError>;
   |     --------------------------------------------------------------------------- expected fn(&row_table::RowTableSlice<'_>, &str) -> std::result::Result<std::collections::HashMap<&value::Value, row_table::RowTableSlice<'_>>, TableError>
   |
   = note: expected `fn(&row_table::RowTableSlice<'_>, &str) -> std::result::Result<std::collections::HashMap<&value::Value, row_table::RowTableSlice<'_>>, TableError>`
              found `fn(&row_table::RowTableSlice<'_>, &str) -> std::result::Result<std::collections::HashMap<&value::Value, row_table::RowTableSlice<'_>>, TableError>`

The expected and found are exactly the same. I can share the full code once I push to GitHub (https://github.com/wspeirs/mem_table). I believe the issue is that in the impl I provide the anonymous lifetime (probably a wrong move on my part) and in the trait I don't provide any lifetime:

pub trait TableOperations<T: TableSlice<T>> { ... snip ... }
impl TableOperations<RowTableSlice<'_>> for RowTableSlice<'_> { ... snip ... }

Tone is always lost in these things... I'm not complaining, and happy to help where I can, just let me know how. Thanks again!

Sometimes using type annotations can help. The compiler will assume what you put is fixed i.e., the priority. Once you get into using this approach, a helpful twist in this technique is to annotate with (); the compiler will generate an error with a message that describes the expected type.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.