如何从RefCell<T>中借用T作为引用?

5
有时我有一个包含在RefCell中的值的结构体,我想要借用这个值,但是我不想让访问器函数的签名依赖于内部实现。为了使其工作,我需要将引用作为Ref而不是&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,那么我会遇到生命周期错误。
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>本身——就像上面的原始代码一样——而不是创建一个新的引用。
有一个回答解决了如何在破坏封装的情况下返回RefCell中的某个内容,虽然从技术上讲可以解决这个问题,但它是一个更为专业化的案例(仅获取RefCell中值的一部分),而且对于这种较简单的情况,解决方案似乎过于复杂。
1个回答

11

这正是impl Trait的目的,它自Rust 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编译器知道实际实现是Ref<T>,但允许您避免显式地编写它,调用此函数的用户只能使用Deref特性提供的功能。

只要您返回的实际值是实现了Deref<Target = T>的类型,您就可以自由地更改该实现,而不会破坏使用它的任何代码。例如,您可以返回&T其他几种引用类型之一,包括自定义类型,如其他链接的问题中所示。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接