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

标签 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作为公共(public) 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 中的一部分值),对于这种更简单的情况,解决方案似乎过于复杂。

最佳答案

这正是 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 编译器知道实际 实现是Ref<T>但让您避免显式编写它,并且此函数的调用者只能使用 Deref 提供的功能特质。

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

关于generics - 如何从 RefCell<T> 中借用 T 作为引用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51349577/

相关文章:

c# - 将类型从字符串变量发送到泛型方法

java - 在java 8下使用泛型类型错误,但不是java 7

java - 通用Java代码难懂?

c++ - 为什么这是允许的?

php - PHP 中的链接引用

rust - 当每晚使用 `cargo run` 和 Rust 1.9 运行时,从 stdin 读取不会读取任何数据

string - 如何交换向量中不可复制的元素?

java - Java 中的泛型,标准值

c# - 引用自动实现属性的支持字段

rust - 为什么不能在同一结构中存储值和对该值的引用?