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 than String
from match_me()
. Apart from the return type, you only need minor changes to match_me()
in your first example:
fn match_me(&self) -> &str {
match &self.data {
Some(x) => x,
None => "match failed",
}
}
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:
fn match_me(&mut self) -> String {
match self.data.take() {
Some(x) => x,
None => "match failed".to_owned(),
}
}
This will leave a None
in self.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:
struct Cell {
data: Option<Rc<String>>,
}
impl Cell {
fn match_me(&self) -> Rc<String> {
match &self.data {
Some(x) => x.clone(),
None => Rc::new("match failed".to_owned()),
}
}
}
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.