Локальная ссылка считается заемной
У меня есть структурный тип с Option<String>
поле. В методе моего необязательного типа я хочу сопоставить это поле и извлечь значение в локальную область. Я понимаю, что мне нужно убедить контролера заимствования не бросать память, на которую указывает мой структурный тип; Я не уверен, как это сделать.
Для контекста, вот явно неправильный пример.
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);
}
Эта программа явно неверна, потому что я перемещаю значение внутри x
в локальную область, что означает, что он будет отброшен при возврате метода; однако, поскольку структура переживает вызов метода, значение все равно будет доступно в другом месте, что приведет к использованию после свободной ошибки.
Так как я хочу использовать Some()
значение, не отбрасывая его, я решил, что я должен ссылаться на это. Попытка два:
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);
}
Однако, несмотря на клонирование этих ссылок, я все еще получаю ошибку заимствования.
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
Неужели у меня нет другого выхода, кроме как clone
сам предмет?
2 ответа
Мне неясно, чего вы пытаетесь достичь, но я могу предложить несколько вариантов работы.
Если вы хотите вернуть ссылку на строку, ничего не меняя в
Cell
, вы должны вернуться&str
скорее, чемString
отmatch_me()
, Помимо возвращаемого типа, вам нужны только незначительные измененияmatch_me()
в вашем первом примере:fn match_me(&self) -> &str { match &self.data { Some(x) => x, None => "match failed", } }
Остальная часть вашего кода может остаться без изменений.
Если вы хотите переместить строку из вашей структуры, вам нужно получить
self
в качестве изменяемой ссылки:fn match_me(&mut self) -> String { match self.data.take() { Some(x) => x, None => "match failed".to_owned(), } }
Это оставит
None
вself.data
после вызова функции, так как мы перемещаем строку и передаем право собственности вызывающей стороне.И, наконец, если по какой-то причине вам действительно требуется совместное владение строкой, вы также можете использовать указатель с подсчетом ссылок:
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()), } } }
Это намного более необычно, чем другие варианты, и ничто в вашем вопросе не намекает на то, что вам это действительно нужно, поэтому я включу это только для полноты.
Мое лучшее предположение, что вы действительно хотите первый вариант.
Я хочу расширить ответ Свена Марнача и предложить еще один вариант, если вы хотите вернуться &String
и избегать clone
impl Cell {
// it is better to use `Result` type in case when an error may be occurred
fn match_me(&self) -> Result<&String, &'static str> {
match self.data {
// `ref` provides to bind a reference to a variable
// cel: &String
Some(ref cel) => Ok(cel),
None => Err("match failed"),
}
}
}
fn main() {
...
// add unwrap to get a value
let result = my_cell.match_me().unwrap();
...
}