代码之家  ›  专栏  ›  技术社区  ›  Peter Hall

如何从refcell中借用t作为引用?

  •  2
  • Peter Hall  · 技术社区  · 8 年前

    有时我有一个 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 | |     }
       | |_____^
    

    似乎没有办法解决这个问题,因为错误消息是正确的。代码试图引用 参考<T> ,仅持续函数调用。为了使这项工作顺利进行,我必须搬出 参考<T> 通过将其返回“与”上面的原始代码完全相同,而不是对其进行新的引用。

    an answer to How do I return a reference to something inside a RefCell without breaking encapsulation? 这在技术上可以解决这个问题,但这是一个更为专业的案例(只获得 引用单元格 )对于这种简单的情况,解决方案似乎过于复杂。

    1 回复  |  直到 7 年前
        1
  •  7
  •   Peter Hall    8 年前

    这正是 impl Trait 已经有了稳定的锈迹 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()
        }
    }
    

    铁锈编译器知道 实际的 实施是 Ref<T> 但是,您可以避免显式地编写它,并且此函数的调用方只能使用 Deref 特质。

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