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

Posted

技术标签:

【中文标题】“不能返回引用临时值的值”和 Rust 中的内部可变性【英文标题】:"cannot return value referencing temporary value" and interior mutability in Rust 【发布时间】:2021-05-19 04:28:15 【问题描述】:

我在 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)
    

正则表达式是惰性计算的,捕获&amp;mut self我不想要,所以使用RefCell

我收到以下消息:

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

编译器消息似乎很清楚:Ref 是由borrow() 临时创建并返回外部的。但是我相信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)
    

我该如何解决?

【问题讨论】:

如果不保留Ref&lt;T&gt;,就无法从RefCell&lt;T&gt; 获得&amp;TRefCell 如何知道何时允许borrow()borrow_mut()。见this Q&A。 【参考方案1】:

您可以通过使用.as_ref()&amp;Option&lt;_&gt; 转换为Option&lt;&amp;_&gt; 来修复Ref::map 版本,以便解包作为参考:

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)
                                              // ^^^^^^^^


在这种情况下,我提倡使用 OnceCell 来自 once_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(在同一个 crate 中)对于惰性求值非常方便。

在playground 上查看。

【讨论】:

以上是关于“不能返回引用临时值的值”和 Rust 中的内部可变性的主要内容,如果未能解决你的问题,请参考以下文章

Rust-线程:使用Sync和Send trait的可扩展并发

Rust-线程:使用Sync和Send trait的可扩展并发

Rust-线程:使用Sync和Send trait的可扩展并发

带有库和二进制文件的 Rust 包?

rust字符串&str和String

如何使用 Rust 和 Amethyst 运行可执行文件