首页 > 解决方案 > “不能返回引用临时值的值”和 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 是惰性计算的,因此使用捕获&mut selfi 不想要RefCell的。

我收到以下消息:

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

编译器消息似乎很清楚:Refborrow()外部临时创建并返回。但是我相信Option( self.regexp_data) 归RefCell结构本身所有,因此在内部使用它应该没问题(因为函数不是pub)。

我还尝试了以下方法(但失败并显示相同的消息)

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

我该如何解决?

标签: rustownershipinterior-mutabilityrefcell

解决方案


Ref::map您可以通过.as_ref()将 转换&Option<_>为 a来修复版本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)
                                              // ^^^^^^^^
}

在这种情况下,我建议OnceCellonce_cell板条箱中使用:

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
    }
}

您可以简单地使用get_or_init来获得相同的效果。OnceCellLazy(在同一个箱子中)对于惰性评估非常方便。

在操场上看到它。


推荐阅读