Borrowing in closures


Hello all. I’m trying to understand borrowing inside closures. I wrote the following small program to illustrate:

fn main() {
    let nums = (1..11).map(|i| i * 2).filter(|&i| i > 5).collect::<Vec<_>>();
    println!("nums = {:?}", nums);

    let nums2 = (0..100).map(|i| i * i).filter(|i| i % 3 == 0).collect::<Vec<_>>();
    println!("nums2 = {:?}", nums2);

The code compiles. My question is why do I need to take in a reference &i in the first closure passed to filter, but not in the 2nd call to filter? In the 2nd call, I could also use &i and the code still compiles, but it’s required in the 1st call. Thanks.


In the first you’re not taking a reference – filter is giving you a reference, and |&i| is pattern-matching through it, so you’re effectively dereferencing it. The type of i ends up just being an integer. This only works because it’s a Copy type, otherwise you’d get an error about moving the value out of a reference.

In |i| the type of i will be left as the reference filter gave you. But Rem is implemented for ref%value for integers, so i % 3 is fine. (I’m not sure why comparisons don’t allow that mismatch.)


It’s not that you aren’t taking a reference in the second case. The closure gets a reference in both cases, but in the first closure you’re dereferencing it when binding to i.

That you can leave the dereferencing out in the second case is because the % operator i.e. the Rem trait has implementations for references as both arguments (i.e. you can do any of 1 % 1, &1 % 1, 1 % &1 or &1 % &1).

The PartialOrd trait used by comparison operators like > does not allow this, although I’m not quite sure why an implementation couldn’t be written.


My guess would be that it is common to compare addresses and to avoid mistakenly converting the type, it was left without an implementation.

As for %, if it didn’t convert, it would mean pointer arithmetic which should be avoided in safe code, that’s my guess why it automatically makes the conversion(dereferencing) here…


There’s no pointer arithmetic in any case; comparing two references will compare the objects, not addresses (if you want to compare addresses, you have to cast to *const T first).

The question is just why there is no impl<T> PartialOrd<&T> for T.


Ok, then it doesn’t make sense :slight_smile:


Thanks @cuviper and @birkenfeld for the detailed answers. I didn’t even know about the Rem trait, and I’m starting to see that traits are central to how everything is implemented in Rust. Now I have a clue on where (and how) to look whenever I run into similar issues, instead of the old “keep trying stuff until the compiler accepts it” approach I was using.