gpt4 book ai didi

rust - 如何临时从结构中借用引用?

转载 作者:行者123 更新时间:2023-12-03 11:48:16 26 4
gpt4 key购买 nike

我正在尝试临时替换块内部结构的引用。但是,即使我恢复了原始引用,借阅检查器仍在提示生命周期问题。
以下代码显示了该问题:

// ExpensiveData is only a placeholder for a much more complex data structure
struct ExpensiveData(i32);
struct ReferenceToED<'a>(&'a mut ExpensiveData);

impl<'a> ReferenceToED<'a> {
fn new(content: &'a mut ExpensiveData) -> Self {
ReferenceToED(content)
}

fn replace(&mut self, new_content: &'a mut ExpensiveData) -> &'a mut ExpensiveData {
std::mem::replace(&mut self.0, new_content)
}

fn get_ref_ed(&self) -> &ExpensiveData {
self.0
}

// Other methods removed for clarity ...
}

fn check_ref_struct() {
let mut ed = ExpensiveData(2);
let mut ref_ed = ReferenceToED::new(&mut ed);

{
let mut ed = ExpensiveData(5);

// Remember the old reference And exchange it with a new one
let tmp = ref_ed.replace(&mut ed);
assert_eq!(5, ref_ed.get_ref_ed().0);

// Restore the old reference
ref_ed.replace(tmp);
assert_eq!(2, ref_ed.get_ref_ed().0);
}

// Although restored I get an error in this line
assert_eq!(2, ref_ed.get_ref_ed().0);
}
错误消息如下:
error[E0597]: `ed` does not live long enough
--> src\lib.rs:29:34
|
29 | let tmp = ref_ed.replace(&mut ed);
| ^^^^^^^ borrowed value does not live long enough
...
35 | }
| - `ed` dropped here while still borrowed
...
38 | assert_eq!(3, ref_ed.get_ref_ed().0);
| ------ borrow later used here
问题:
  • 我如何说服借阅检查器这是安全代码? (除了使用不安全的代码外,还有其他原因)
  • 是否有一种典型的模式可以遵循以处理这些类型的问题?
  • 最佳答案

    经过一段时间的试验,我来到了下面的解决方案。我使用了一种ReferenceToEDManager结构来管理临时交换的数据:

    pub struct ReferenceToEDManager<'reference_to_ed, 'data, 'tmp> {
    reference_to_ed: &'reference_to_ed mut ReferenceToED<'data>,
    orig_data: Option<&'data mut ExpensiveData>,
    _pd_td: marker::PhantomData<&'tmp mut ExpensiveData>,
    }

    impl<'reference_to_ed, 'data, 'tmp>
    ReferenceToEDManager<'reference_to_ed, 'data, 'tmp>
    {
    pub fn new(
    reference_to_ed: &'reference_to_ed mut ReferenceToED<'data>,
    new_data: &'tmp mut ExpensiveData,
    ) -> Self {
    let new_data_ptr = ptr::NonNull::new(new_data).unwrap();

    // SAFETY: It is possible, to replace the ExpensiveData reference with a
    // shorter living reference of new_data in ReferenceToED, because
    // the lifetime of self is at most the minimal lifetimes of:
    // - orig_data and
    // - reference_to_ed
    // and because the Drop implementation of Self restores the old
    // configuration of reference_to_ed
    let orig_data = reference_to_ed
    .replace(unsafe { &mut *new_data_ptr.as_ptr() });
    Self {
    reference_to_ed,
    orig_data: Some(orig_data),
    _pd_td: marker::PhantomData,
    }
    }

    pub fn get_reference_to_ed(&mut self) -> &mut ReferenceToED<'tmp> {
    let reference_to_ed_pointer =
    ptr::NonNull::new(self.reference_to_ed)
    .unwrap()
    .cast::<ReferenceToED<'tmp>>();

    // SAFETY: It is safe to return a reference to the `ReferenceToED`
    // with tmp_data lifetime for the contained data, because during
    // construction of self a reference with this lifetime has been
    // inserted in the `ReferenceToED`
    unsafe { &mut *reference_to_ed_pointer.as_ptr() }
    }
    }

    impl Drop for ReferenceToEDManager<'_, '_, '_> {
    /// Replaces the temporary reference of `ExpensiveData` whith the original one.
    ///
    /// # Safety
    ///
    /// `ReferenceToED::replace(...)` shall replace the referenced
    /// `ExpensiveData` with the one supplied as argument.
    fn drop(&mut self) {
    let orig_value = self.orig_data.take().unwrap();
    self.reference_to_ed.replace(orig_value);
    }
    }

    #[cfg(test)]
    mod test {
    use super::super::expensive_data::{ExpensiveData, ReferenceToED};
    use super::ReferenceToEDManager;
    #[test]
    fn check_ref_struct() {
    let mut ed = ExpensiveData(2);
    let mut ref_ed = ReferenceToED::new(&mut ed);
    {
    let mut ed = ExpensiveData(5);
    // Remember the old reference And exchange it with a new one
    let mut ed_manager =
    ReferenceToEDManager::new(&mut ref_ed, &mut ed);
    // NOTE: It is impossible to use ref_ed from this point on
    // any more, thanks to Rust borrowing rules!
    // assert_eq!(5, ref_ed.get_ref_ed().0);
    let ref_ed_ref = ed_manager.get_reference_to_ed();
    assert_eq!(5, ref_ed_ref.get_ref_ed().0);

    // Restore the old reference
    }
    // Although restored I get an error in this line
    assert_eq!(2, ref_ed.get_ref_ed().0);
    }
    }
    我很确定,没有任何 unsafe块就不可能编写此代码,因为它需要对生命周期进行一些操作。

    关于rust - 如何临时从结构中借用引用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62969038/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com