Fighting the borrow checker: unwrap Vs match


#1

I am still being bamboozled by the borrow checker. Clearly I am missing something…

Given two similar programmes one that uses unwrap to access a value in a Option and one that uses match. The one that uses match has no problems whereas unwrap does.

I thought that both cases are equivalent, what am I missing?

I get a borrow checker error with this version:

use std::collections::HashMap;
struct S {
    p:Option<HashMap<String, usize>>, 
}

impl S {
    #[allow(dead_code)] 
    fn cnt(&self) {
        let c = &self.p;
        let c = c.unwrap();
        let _c = c.keys().count();
    }
}
fn main() {
    let _s = S{p:Some(HashMap::new())};
    
}
10 |         let c = c.unwrap();
   |                 ^ cannot move out of borrowed content

This one compiles

use std::collections::HashMap;
struct S {
    p:Option<HashMap<String, usize>>, 
}

impl S {
    #[allow(dead_code)]
    fn cnt(&self) {
        match &self.p {
            Some(c) => {
                let _c = c.keys().count();
            },
            None => (),
        }
    }
}
fn main() {
    let _s = S{p:Some(HashMap::new())};
    
}


#2

You want to use as_ref() in your first example:

let c = c.as_ref().unwrap();

unwrap() takes self which means it wants to move/consume it, but you have only a borrow of the Option field. as_ref() gives you an owned Option that has a ref to the value inside, and you can unwrap that.

The match statement is using match ergonomics and the Some arm is essentially:

&Some(ref c) => ...

#3

Ok. I see that I only have a borrow of the option field (the &self in the signature of cnt). I do not see why unwrap needs to take ownership. On the LHS c is immutable so why does it not do a immutable borrow? What prevents it doing that? What is the reason for it?

I am happy, I learnt of Option::as_ref and I see what it does, unwrap returns what ever is in the option transferring ownership. Which is OK with a immutable reference but not a value. But I am unhappy because I do not see why.

Possibly I understand less than I think!


#4

c is &Option<HashMap<...>>. The signature of unwrap takes self, as mentioned. The dot operator, such as when calling a method, automatically dereferences through the reference. So you end up trying to consume/move a borrowed Option.

I’m not sure if that clears it up or not though.


#5

Food for thought. Thank you.

The learning curve for Rust I find, is shallow but very long! Or should that be short but very steep? Taking me a long time to get my head around a few concepts. This helped.


#6

Yeah, the curve can be both :slight_smile:.

Try asking more questions here - someone might be able to phrase/formulate an answer that sits better with you than whatever other study material you’re using.