本地参考资料被视为借用

时间:2018-09-27 20:49:50

标签: rust borrow-checker borrowing lifetime-scoping

我有一个带有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内的值移到了本地范围内,这意味着当方法返回时它将被删除;但是,由于该结构的寿命超过了方法调用的时间,因此该值仍可在其他位置访问,这将在释放错误后产生 use 错误。

由于我想使用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 Link to the obviously wrong example.

Playground Link to a reference counted nightmare.

2 个答案:

答案 0 :(得分:4)

我不清楚您要达到的目标,但是我可以提供一些可行的选择。

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

    与其他选项相比,这非常不常见,您的问题中没有任何内容暗示您实际上需要此功能,因此出于完整性考虑,我仅将其包括在内。

我最好的猜测是您实际上想要第一个选择。

答案 1 :(得分:0)

如果您想返回&String并避免使用clone,我想扩展Sven Marnach的答案并提出另一种选择

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();
    ...
}