Difference between Closure and Function Reference

Given this:

let v = vec!("1", "2", "3");

Why does this work:

let v2 : Vec<Result<u32,ParseIntError>> = v.iter().map( |s| s.parse() ).collect();

But this fails:

let v2 : Vec<Result<u32,ParseIntError>> = v.iter().map(str::parse).collect();
error[E0631]: type mismatch in function arguments
 --> main.rs:5:58
  |
5 | ...().map(str::parse).collect();
  |           ^^^^^^^^^^
  |           |
  |           expected signature of `fn(&&str) -> _`
  |           found signature of `for<'r> fn(&'r str) -> _`

I thought those two were roughly equivalent, but there's clearly a difference, and I'm trying to better understand that difference. ;). Now, because the strings are literal, that means they're &str to begin with and by using iter, I believe they get borrowed again, so I guess I see why it's expecting &&str ... but why doesn't the closure version get the same error?

The . (dot) operator can deref through an arbitrary number of layers of indirection, so when you do a method call as in s.parse() you deref past all reference wrappers, but by passing the method directly, you have to give it the exact number of layers it expects.

For that same reason, {&&&&"abc"}.len() will work fine, but str::len(&&&&"abc") will not, because Deref won't be called automatically without the ..

2 Likes

The type of s in .map( |s| s.parse() ) is &&str. Calling s.parse() will auto-dereference it so you end up calling the parse method on an &str, which works.
But since you're map-ing an iterator over &&strs, doing .map(str::parse) fails because str::parse doesn't work with an &&str.

You can get around this by converting the &&str to a &str with the .copied() adaptor:

let v2 : Vec<Result<u32,ParseIntError>> = v.iter().copied().map(str::parse).collect();
3 Likes

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.