简体   繁体   English

Rust 中的“无法返回引用临时值的值”和内部可变性

[英]“cannot return value referencing temporary value” and interior mutability in Rust

I have the following code in Rust:我在 Rust 中有以下代码:

pub struct RegExpFilter {
    ...
    regexp_data: RefCell<Option<RegexpData>>,
    ...
}

struct RegexpData {
    regexp: regex::Regex,
    string: String
}

...
    pub fn is_regexp_compiled(&self) -> bool {
        self.regexp_data.borrow().is_some()
    }

    pub fn compile_regexp(&self) -> RegexpData {
        ...
    }

    fn regexp(&self) -> &regex::Regex {
        if !self.is_regexp_compiled() { // lazy computation that mutates the struct
            self.regexp_data.replace(Some(self.compile_regexp()));
        }
        &self.regexp_data.borrow().as_ref().unwrap().regexp
    }
    
    pub fn matches(&self, location: &str) -> bool {
         self.regexp().find(location)
    }

regexp is calculated lazily, capturing &mut self i undesired so RefCell is used.正则表达式是惰性计算的,捕获&mut self i 不想要的,因此使用RefCell

I'm getting the following message:我收到以下消息:

               &self.regexp_data.borrow().as_ref().unwrap().regexp
    |          ^-------------------------^^^^^^^^^^^^^^^^^^^^^^^^^
    |          ||
    |          |temporary value created here
    |          returns a value referencing data owned by the current function

The compiler message seems to be clear: Ref is temporarily created by borrow() and returned outside.编译器的信息似乎很清楚: Ref是由borrow()临时创建并返回到外部的。 However i believe Option ( self.regexp_data ) is owned by RefCell which is owned by the struct itself, so it should be fine to use it internally (since the function is not pub ).但是我相信Optionself.regexp_data )归结构本身所有的RefCell所有,所以在内部使用它应该没问题(因为 function 不是pub )。

I've also tried the following (and it fails with the same message)我还尝试了以下方法(但失败并显示相同的消息)

    fn regexp(&self) -> impl Deref<Target = regex::Regex> + '_ {
        if !self.is_regexp_compiled() {
            self.regexp_data.replace(Some(self.compile_regexp()));
        }
        Ref::map(self.regexp_data.borrow(), |it| &it.unwrap().regexp)
    }

How can i solve it?我该如何解决?

You can fix the Ref::map version by using .as_ref() to convert the &Option<_> to a Option<&_> in order to to unwrap as a reference:您可以修复Ref::map版本,方法是使用.as_ref()&Option<_>转换为Option<&_>以便解包作为参考:

fn regexp(&self) -> impl Deref<Target = regex::Regex> + '_ {
    if !self.is_regexp_compiled() {
        self.regexp_data.replace(Some(self.compile_regexp()));
    }
    Ref::map(self.regexp_data.borrow(), |it| &it.as_ref().unwrap().regexp)
                                              // ^^^^^^^^
}

In this scenario, I'd advocate for using OnceCell from the once_cell crate:在这种情况下,我提倡使用OnceCell板条箱中的OnceCell

use once_cell::sync::OnceCell;

pub struct RegexpData {
    regexp: regex::Regex,
    string: String,
}

pub struct RegExpFilter {
    regexp_data: OnceCell<RegexpData>,
}

impl RegExpFilter {
    pub fn compile_regexp(&self) -> RegexpData {
        unimplemented!()
    }

    fn regexp(&self) -> &regex::Regex {
        &self.regexp_data.get_or_init(|| self.compile_regexp()).regexp
    }
}

You can simply use get_or_init to get the same effect.您可以简单地使用get_or_init来获得相同的效果。 OnceCell and Lazy (in the same crate) are very convenient for lazy-evaluation. OnceCellLazy (在同一个 crate 中)对于惰性求值非常方便。

See it on the playground .操场上看到它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM