Реализация Deref для структуры контейнера

#rust

#Ржавчина

Вопрос:

У меня есть следующий код, который реализует какую-то отложенную инициализацию, и я хочу использовать внутреннюю изменчивость и реализацию Deref для удобства.

 pub struct Deferred<T> {
    value: RefCell<Value<T>>
}

pub enum Value<T> {
    Initialized(T),
    WaitingForValue
}

impl<T> Deferred<T> {
    pub fn init_later() -> Self {
        Self { value: RefCell::new(WaitingForValue) }
    }
    pub fn init(amp;self, value: T) {
        self.value.replace(Initialized(value));
    }
}

impl<T> Deref for Deferred<T> {
    type Target = T;

    fn deref(amp;self) -> amp;Self::Target {
        if let Initialized(value) = self.value.borrow().deref() {
            value
        } else {
            panic!("Deferred value must be initialized before the first usage")
        }
    }
}
  

Но у меня ошибка:

 error[E0515]: cannot return value referencing temporary value
   |
30 |         if let Initialized(value) = self.value.borrow().deref() {
   |                                     ------------------- temporary value created here
31 |             value
   |             ^^^^^ returns a value referencing data owned by the current function
  

Я пробовал разные ссылочные типы, но не могу заставить это работать.

Кроме того, я пытался сделать это с помощью unsafe:

 fn deref(amp;self) -> amp;Self::Target {
    unsafe {
        if let Initialized(value) = amp;*self.value.as_ptr() {
            amp;value
        } else {
            panic!("Deferred value must be initialized before the first usage")
        }
    }
}
  

В этом случае это работает, но я не уверен, что этот небезопасный код не приводит к неопределенному поведению.

Ответ №1:

Вы не можете вернуть общую ссылку из a RefCell , потому что проверка заимствования выполняется во время выполнения с помощью Ref и RefMut , этой реализации-оболочки as_ref<Inner> , но созданные ссылки принадлежат оболочке ( Ref / RefMut ), поэтому вы должны сохранить их в хранилище.

Это усложняет использование функций deref и as_ref , но вы можете добиться такого же поведения, например, внедрив Deref on Value<T> вместо Deffered

 use std::{cell::{RefCell}, ops::{Deref}};

pub struct Deferred<T> {
    value: RefCell<Value<T>>
}

pub enum Value<T> {
    Initialized(T),
    WaitingForValue
}

impl<T> Deferred<T> {
    pub fn init_later() -> Self {
        Self { value: RefCell::new(Value::WaitingForValue) }
    }
    pub fn init(amp;self, value: T) {
        self.value.replace(Value::Initialized(value));
    }
}

/// Implement deref on `Value` instead of `Deferred`
impl<T> Deref for Value<T> {
    type Target = T;

    fn deref(amp;self) -> amp;Self::Target {
        if let Value::Initialized(value) = self {
            value
        } else {
            panic!("Deferred value must be initialized before a first usage")
        }
    }
}

fn main() {
    let val = Deferred::init_later();
    val.init(String::from("Hello, world !"));
    let _: amp;String = val.value.borrow().deref();
}