Consider this rust code: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=d6f2075a8872305334a8ba513241950b
fn main() {
let v: Vec<i32> = vec![1, 2, 3];
// This works, of course.
println!("{}", foo(&v));
// Now let's create an "extra conversion" step:
let vs: Vec<&str> = vec!["1", "2", "3"];
// We want to "stream" straight from this vec. Let's create an
// iterator that converts:
let converting_iterator = vs.iter().map(|s| s.parse::<i32>().unwrap());
// This does not work
println!("{}", foo(converting_iterator));
}
fn foo<'a>(it: impl IntoIterator<Item=&'a i32>) -> i32 {
it.into_iter().sum()
}
I understand why the second line doesn't work. It creates an iterator over i32
, not &i32
. I can't just slap a &
into the closure, because that would attempt to reference a temporary value.
What I'd be curious about though is if there is any way to write foo
in such a way that it can deal with both types of iterables? If I were to just add the .sum()
to the end of creating the converting_iterator
, it would just work. So I feel that there should be some way to "intercept" the result (i.e. the converting iterator), pass that to something, and have that something call .sum
on it.
Maybe something with Borrow
or AsRef
, but I couldn't figure that out from the documentation of those traits.
A more broad answer is to use the Borrow
trait to accept references and non-references generically:
use std::borrow::Borrow;
fn foo<'a, T: Borrow<i32>>(it: impl IntoIterator<Item = T>) -> i32 {
it.into_iter().map(|t| *t.borrow()).sum()
}
It is unfortunately not as nice as PitaJ's answer since .sum()
is already fine with both references and non-references, but doesn't handle impl Borrow
, so we have to introduce a .map()
to get the integers out of the potentially-borrowed type. But this is a trick to use if your function was different.
This is a pattern seen in other questions: