如何从RefCell <t>借用T作为参考?

时间:2018-07-15 15:01:00

标签: generics reference rust borrow-checker

有时我有一个struct,其中包含一个包裹在RefCell中的值,我想借用该值,但是我不想使访问器函数的签名依赖在内部执行。为了使其正常工作,我需要以Ref<T>而不是&T的形式返回引用。

例如,如果这是我的结构:

use std::cell::RefCell;

pub struct Outer<T> {
    inner: RefCell<T>,
}

我可以这样编写访问器:

use std::cell::Ref;

impl<T> Outer<T> {
    fn get_inner_ref(&self) -> Ref<T> {
        self.inner.borrow()
    }
}

这很好。我可以这样使用它:

fn main() {
    let outer = Outer { inner: RefCell::new(String::from("hi")) };
    let inner: &str = &outer.get_inner_ref();
    println!("inner value = {:?}", inner);
}

但是,这将Ref作为公共API的一部分公开,这将使得以后更难更改内部结构而又不破坏向后兼容性。

如果我尝试更改签名以返回&T(可以强制转换为&Ref<T>),那么我会收到终身错误:

impl<T> Outer<T> {
    fn get_inner_ref(&self) -> &T {
        &self.inner.borrow()
    }
}

错误是:

error[E0597]: borrowed value does not live long enough
  --> src/main.rs:16:10
   |
16 |         &self.inner.borrow()
   |          ^^^^^^^^^^^^^^^^^^^ temporary value does not live long enough
17 |     }
   |     - temporary value only lives until here
   |
note: borrowed value must be valid for the anonymous lifetime #1 defined on the method body at 15:5...
  --> src/main.rs:15:5
   |
15 | /     fn get_inner_ref(&self) -> &T {
16 | |         &self.inner.borrow()
17 | |     }
   | |_____^

似乎没有办法解决此问题,因为错误消息是正确的。代码试图引用对Ref<T>的引用,该引用仅持续到函数调用的时间。为了使这项工作有效,我必须移回Ref<T>本身,就像上面的原始代码一样,返回它,而不是对其进行新引用。

an answer to How do I return a reference to something inside a RefCell without breaking encapsulation?可以从技术上解决此问题,但这是一个更特殊的情况(仅获得RefCell中一部分值),对于这种简单情况,解决方案似乎过于复杂

1 个答案:

答案 0 :(得分:7)

这正是impl Trait的目的,它已在稳定的Rust since version 1.26中提供。

use std::ops::Deref;

impl<T> Outer<T> {
    fn get_inner_ref<'a>(&'a self) -> impl Deref<Target = T> + 'a {
        self.inner.borrow()
    }
}

Rust编译器知道 actual 实现是Ref<T>,但是可以避免必须显式地编写它,并且此函数的调用者只能使用{{1}提供的功能}。

只要您返回的实际值是实现Deref的类型,您以后就可以随意更改该实现,而不会破坏任何使用它的代码。例如,您可以返回Deref<Target = T>one of several other reference types,包括您自己的自定义类型,如other linked question所示。