I have come across some confusing behavior of smart pointers and how it relates to de-referencing etc and I am pretty sure this is due to gaps in my understanding of how they work. Hence this question and looking forward to getting some clarification.
I will be using Box
and Rc
but my guess is the same confusion I have applies to other smart pointers.
Q1. I can transfer ownership and move a value into a smart pointer like Box
. Can I move the value out and transfer ownership out? If not why not?
For example in the code below
#[derive(Debug)]
enum MyBool {
True,
False
}
let my_value: MyBool = MyBool::True;
let value_in_box = Box::new(my_value); // ownership goes from my_value to Box
// println!("{:?}", my_value); won't compile. Used of moved value
let my_value = ??? // is it possible to transfer ownership from Box back to my_value?
I moved a value originally owned by my_value
into the Box
. Is there any syntax to move this value out of the Box
back to my_value
?
Q2. I can use a value in a Box
as if it was not in a Box
, but why can't I pass a value in a Box
to function expecting the value without a Box
?
In the same theme of accessing value within a smart pointer like Box
(first question was about accessing it and moving it out). This question is about using the value.
I realize if I have a value in a Box
, I can work with the value transparently as if it were not in a Box
. For example:
let my_value: u8 = 2;
let value_in_box = Box::new(my_value);
let pow_two = value_in_box.pow(2);
I was able to call the pow
method on value_in_box
as if it were not in a Box
. But if I have a function defined to take in u8
I cannot just pass in value_in_box
. For example the following code won't work:
fn pow_2(i: u8) -> u8 {
i.pow(2)
}
let my_value: u8 = 2;
let value_in_box = Box::new(my_value);
pow_2(value_in_box)
It will complain about mismatched type. Also a bit confusing is the fact that trying to use the value below won't work too.
let my_value: u8 = 2;
let value_in_box = Box::new(my_value);
value_in_box * 2;
One can interact with methods on the value in the Box
transparently in some ways (ie when calling methods on it) but cannot in some other ways (ie when passing into a function or using it with operands like *)
Why is this distinction? And what is the best way to think about accessing values inside a smart pointer like Box
?
Q3. What is the benefit of putting values in hashmap into a Rc
?
I have come across advice to put values in a hashmap into Rc
. By default if a value is gotten from a hashmap using .get
one get's a reference. The only way to get a own copy is either to clone or use the remove
method to get the value out of the hashmap.
The advice to use Rc to wrap value is given as way to have the value remain in the HashMap
but also own the value elsewhere.
Trying this advice out, I end up with the following code:
#[derive(Clone)]
struct Person {
username: String,
is_admin: bool
}
fn main() {
let person = Person {
username: "alice".to_string(),
is_admin: true
};
let mut rced_values: HashMap<String, Rc<Person>> = HashMap::new();
rced_values.insert("Alice".to_string(), Rc::new(person.clone()));
let retrieved_rc: &Rc<Person> = rced_values.get("Alice").unwrap();
let mut raw_values: HashMap<String, Person> = HashMap::new();
raw_values.insert("Alice".to_string(), person);
let retrieved_raw: &Person = raw_values.get("Alice").unwrap();
}
But I am not sure what the use was in having the values in Rc
in the first HashMap? How is getting retrieved_rc: &Rc<Person>
out of the map better than getting retrieved_raw: &Person
?