Локальная ссылка считается заемной - PullRequest
0 голосов
/ 27 сентября 2018

У меня есть структурный тип с полем 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 в локальную область, что означает, что он будет отброшен при возврате метода;однако, поскольку структура переживает вызов метода, значение все равно будет доступно в другом месте, что приведет к использованию после ошибки free .

Поскольку я хочу использовать значение 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 самого предмета?

Playground Ссылка на явно неправильный пример.

Детская площадка Ссылка на счетный кошмар.

Ответы [ 2 ]

0 голосов
/ 28 сентября 2018

Я хочу расширить ответ Свена Марнача и предложить еще один вариант, если вы хотите вернуть &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();
    ...
}
0 голосов
/ 28 сентября 2018

Мне неясно, чего вы пытаетесь достичь, но я могу предложить несколько вариантов работы.

  1. Если вы хотите только вернуть ссылку на строку без изменениячто-нибудь в Cell, вы должны вернуть &str вместо String из match_me().Помимо типа возврата, вам нужно только незначительные изменения в match_me() в первом примере:

    fn match_me(&self) -> &str {
        match &self.data {
            Some(x) => x,
            None => "match failed",
        }
    }
    

    Остальная часть вашего кода может остаться без изменений.

  2. Если вы хотите переместить строку из вашей структуры, вам нужно получить self в качестве изменяемой ссылки:

    fn match_me(&mut self) -> String {
        match self.data.take() {
            Some(x) => x,
            None => "match failed".to_owned(),
        }
    }
    

    Это оставит None в self.data после вызова функции, так как мывынимаем строку и передаем право собственности вызывающей стороне.

  3. И, наконец, если по какой-то причине вам действительно требуется совместное владение строкой, вы также можете использовать указатель с подсчетом ссылок:

    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()),
            }
        }
    }
    

    Это гораздо более необычно, чем другие варианты, и ничто в вашем вопросе не намекает на то, что вам это действительно нужно, поэтому я включаю это только для полноты.

Мое лучшее предположение, что вам действительно нужен первый вариант.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...