Function that converts Iterator of tuple of AsRef<str> to Vec of tuples of &str

Rust Playground (rust-lang.org)

In this example, the function has a signature of:

fn foo<'a, S>(iter: impl IntoIterator<Item = &'a (S, S)>) -> Vec<(&'a str, &'a str)>
where
    S: AsRef<str> + 'a,

This seems to work in cases where the function is called as so:

    let v_str = vec![("A", "1"), ("B", "2"), ("C", "3"), ("D", "4")];
    let v_string: Vec<(String, String)> = v_str
        .iter()
        .map(|(s1, s2)| (s1.to_string(), s2.to_string()))
        .collect();

    dbg!(foo(v_str.iter()));
    dbg!(foo(v_string.iter()));

but fails without the extra .iter():

error[E0271]: type mismatch resolving `<Vec<(&str, &str)> as IntoIterator>::Item == &(_, _)`
  --> src/main.rs:8:14
   |
8  |     dbg!(foo(v_str));
   |          --- ^^^^^ expected `&(_, _)`, found `(&str, &str)`
   |          |
   |          required by a bound introduced by this call
   |
   = note: expected reference `&(_, _)`
                  found tuple `(&str, &str)`

Is there something I can do here to avoid needing the extra .iter() in the function call? It looks like without it, the iter will return (S, S) instead of &(S, S).

Pass in references:

    dbg!(foo(&v_str));
    dbg!(foo(&v_string));

By convention, an inherent fn iter(&self) -> .. method corresponds to an implementation of IntoIterator for &Self.

1 Like

the problem is indeed the explicit reference of the tuple. but your code should work with slightly change, just use &v_str instead of v_str:

    dbg!(foo(&v_str));
    dbg!(foo(&v_string));

because IntoIterator is implemented for slices and &Vec:

Instead of passing a reference, is it at all possible to modify foo so that it'd accept both a reference and the actual value? Can use AsRef on the IntoInterator somehow?

For one, why force taking ownership when you don't need it? But for two, it's impossible to pass a Vec<(String, String)> by value and get a Vec<(&str, &str)> to the contents back out (without leaking the values). The Vec and contained Strings would drop at the end of foo and leave the results dangling.

Incidentally, if you try to be maximally generic in ways that are possible, you may regret it anyway -- adding too many layers of indirection will stymie inference, and you'll end up needing to use turbofish or similar at the call site, making things less ergonomic -- not more.

3 Likes

I'm afraid the answer is no.

no matter what type signature the function has, if it accept the argument by value at the callsite (call it like foo(arg),), the argument will be moved, then you cannot return references with lifetimes derived from it.

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.