I've got a structural type with an Option<String>
field. Within a method on my optional type, I want to match on that field and extract the value into the local scope. I understand that I need to convince the borrow checker not to drop the memory pointed to within my structural type; I'm not sure how to do that.
For context, here's an obviously wrong example.
struct Cell {
data: Option<String>,
}
impl Cell {
fn match_me(&self) -> String {
match self.data {
Some(x) => x,
None => "match failed".to_owned(),
}
}
}
fn main() {
let data = Some("hello".to_owned());
let my_cell = Cell { data };
let result = my_cell.match_me();
print!("{}", result);
}
This program is obviously wrong because I'm moving the value inside of x
into the local scope, which means it will be dropped when the method returns; however, since the struct outlives the method call, the value will still be accessible elsewhere, which would produce a use after free error.
Since I want to use the Some()
value without discarding it, I figured I should reference count it. Attempt two:
use std::rc::Rc;
struct Cell {
data: Rc<Option<Rc<String>>>,
}
impl Cell {
fn match_me(&self) -> String {
let local = self.data.clone();
match *local {
Some(x) => *Rc::clone(&x),
None => "match failed".to_owned(),
}
}
}
fn main() {
let data = Rc::new(Some(Rc::new("hello".to_owned())));
let my_cell = Cell { data };
let result = my_cell.match_me();
print!("{}", result);
}
However, despite cloning these references, I'm still getting the borrow error.
Compiling playground v0.0.1 (file:///playground)
error[E0507]: cannot move out of borrowed content
--> src/main.rs:10:15
|
10 | match *local {
| ^^^^^^ cannot move out of borrowed content
11 | Some(x) => *Rc::clone(&x),
| - hint: to prevent move, use `ref x` or `ref mut x`
error[E0507]: cannot move out of borrowed content
--> src/main.rs:11:24
|
11 | Some(x) => *Rc::clone(&x),
| ^^^^^^^^^^^^^^ cannot move out of borrowed
content
Do I really have no recourse except to clone
the item itself?
It is unclear to me what you are trying to achieve, but I can offer a few options that work.
If you only want to return a reference to the string without changing anything in
Cell
, you should return&str
rather thanString
frommatch_me()
. Apart from the return type, you only need minor changes tomatch_me()
in your first example:The rest of your code can remain unchanged.
If you want to move the string out of your structure, you need to receive
self
as mutable reference:This will leave a
None
inself.data
after calling the function, since we are moving the string out and transferring ownership back to the caller.And finally, if for some reason you really need shared ownership of the string, you can also use a reference-counted pointer:
This is a lot more uncommon than the other options, and nothing in your question hints that you actually need this, so I'm only including this for completeness.
My best guess is that you actually want the first option.