Search code examples
rustsmart-pointersdereferencereference-counting

Calling map on an Option<Rc<Struct>> works differently than calling it on a Option<Rc<i32>>


I'm new to Rust and I'm trying to figure out why Rc is behaving differently when being passed to a closure. My full code is the following:

use std::rc::Rc;

struct Something {
    value: i32
}

fn main() {
    let wrapped_struct = Some(Rc::new(Something { value: 1 }));
    let wrapped_integer = Some(Rc::new(1));
    // Case 1: This works
    let works: Option<i32> = wrapped_struct.map(|i| { i.value });
    // Case 2: This fails
    let fails: Option<i32> = wrapped_integer.map(|i| { i });
}

The error message is:

   |
13 |     let fails: Option<i32> = wrapped_integer.map(|i| { i });
   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected i32, found struct `std::rc::Rc`
   |
   = note: expected type `std::option::Option<i32>`
              found type `std::option::Option<std::rc::Rc<{integer}>>`

What I don't understand is why in the first closure (Case 1) I can use i as a Something (I'd expect Rc<Something>) but in the second one (Case 2) I can't use i as an i32 (I actually get an Rc<i32>).

I appreciate any pointers to the relevant documentation. Thanks a lot!


Solution

  • The type of the i in both closures is actually Rc<Something> and Rc<i32> respectively. Rc can be dereferenced to access its inner data, but there are places in Rust where dereferencing happens automatically, for convenience.

    In the struct case, when you write i.value, it will automatically dereference i to access the field. It then returns a copy of the i32, because i32 is a Copy type. So the type of the expression i.value is i32. It's as if you wrote (*i).value, but Rust did the dereferencing for you.

    In the i32 case, you are just returning the i, which still has type Rc<i32>. You can fix it by explicitly dereferencing:

    wrapped_integer.map(|i| { *i });
    

    See also: