Why does capturing a reference by reference gives me the actual object?

Hi, in the following example

let mut v: Vec<String> = vec!["abc".to_string()];

for i in v {
    let cmp = "abc".to_string();
    
    v.retain(|x| x != cmp);
}

I get an error on the retain line: can't compare &std::string::String with std::string::String. This is expected since the retain lambda is passed a reference to each item and cmp is a String.

Howerver, if I capture the item by reference:

    v.retain(|&x| x != cmp);

the actual type of x becomes String and not &&String as I would expect. How come? Thanks

That's not capturing it by reference. That's a pattern in the argument.

1 Like

You are removing the reference using pattern matching (example in the book), to take a reference use ref (rust-by-example).

You can think of this not as "capturing by something", but as "taking as something". In this case, the closure takes some reference to the object and assigns, or binds, the name x to that object.

Note that your code wouldn't compile as-is (playground) - for two reasons, but I'll focus on the first one:

error[E0507]: cannot move out of a shared reference
 --> src/main.rs:7:19
  |
7 |         v.retain(|&x| x != cmp);
  |                   ^-
  |                   ||
  |                   |data moved here
  |                   |move occurs because `x` has type `std::string::String`, which does not implement the `Copy` trait
  |                   help: consider removing the `&`: `x`

It means that, when binding the item behind the reference to the name, program must copy it - otherwise it'd have to "steal" it from the real owner; but the String, as any container, isn't copyable.

1 Like

It looks like you don't know how closures are desugared, please read my blog post to see how closures are just fancy structs.

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