首页 > 解决方案 > 如何告诉编译器在不删除整个结构的情况下释放结构中的借用?

问题描述

我有以下结构,它表示数字计算的计划:

pub struct NfftPlan<'a> {
    x: Option<&'a [f64]>,
    f_hat: Option<&'a [Complex64]>,
    // ...
}

它有一个set_f_hat方法:

pub fn set_f_hat(&mut self, f_hat: &'a [Complex64]) {
    self.f_hat = Some(f_hat);
}

和一个execute方法:

pub fn execute(&self) -> Vec<Complex64>

它使用f_hat不变。

我想通过以下方式使用它:

let mut f_hat = vec![1,2,3,4];
let plan = NfftPlan::new()
plan.set_f_hat(&f_hat);
plan.execute();
f_hat[0] = 3; // Change f_hat to a new value
plan.execute(); //New computation

f_hat这失败了,因为我在plan仍然存在时无法变异。有没有办法plan释放f_hat允许我改变f_hat向量的借用?像这样的东西:

releasedata(&self) {
    self.f_hat = None
} //Now the compiler forgets that plan would hold an borrow to f_hat

我知道 Rust 不允许我在存在借用时更改向量,在这种情况下是通过结构f_hat中的引用NfftPlan。我想要一种方法来告诉编译器在NfftPlan不删除整个结构的情况下删除对结构中向量的引用。

标签: rustborrow-checker

解决方案


解释

我如何告诉编译器释放借用

你不能,期间。这不是你“告诉”编译器的事情,编译器知道一切。您只能完全停止使用该参考。

不删除整个结构

Dropping不会清除借用,只有不再使用的借用才会清除,这可能发生在 drop 中。

f_hat[0] = 3; // Change f_hat to a new value
plan.execute(); //New computation

正是Rust 试图阻止的代码类型之一。应该返回不同的值一点也不明显,plan.execute()因为一些明显不相关的值已经改变。

解决方案

在类型系统中对其进行编码

我会构建我的类型以反映它们需要如何使用,创建一次性值,这些值只有在所有内容组合在一起后才能执行。这意味着借用的结构f_mut一旦完成就会被删除;请注意这如何Option完全删除:

fn main() {
    let mut f_hat = 42;

    let plan = Plan::default();
    plan.set_f_hat(&f_hat).execute();

    f_hat = 3;
    plan.set_f_hat(&f_hat).execute();
}

#[derive(Debug, Default)]
struct Plan<'a> {
    x: Option<&'a i32>,
}

impl<'a> Plan<'a> {
    fn set_f_hat(&self, f_hat: &'a i32) -> PlanPlus<'a> {
        PlanPlus { x: self.x, f_hat }
    }
}

#[derive(Debug)]
struct PlanPlus<'a> {
    x: Option<&'a i32>,
    f_hat: &'a i32,
}

impl<'a> PlanPlus<'a> {
    fn execute(&self) {}
}

使用内部可变性和引用计数

use std::{cell::Cell, rc::Rc};

#[derive(Debug, Default)]
struct Plan<'a> {
    x: Option<&'a i32>,
    f_hat: Option<Rc<Cell<i32>>>,
}

impl<'a> Plan<'a> {
    fn set_f_hat(&mut self, f_hat: Rc<Cell<i32>>) {
        self.f_hat = Some(f_hat);
    }
    fn execute(&self) {}
}

fn main() {
    let f_hat = Rc::new(Cell::new(42));

    let mut plan = Plan::default();
    plan.set_f_hat(f_hat.clone());
    plan.execute();

    f_hat.set(3);
    plan.execute();
}

认识到成员是可变的

#[derive(Debug, Default)]
struct Plan<'a> {
    x: Option<&'a i32>,
    f_hat: Option<&'a mut i32>,
}

impl<'a> Plan<'a> {
    fn f_hat(&mut self) -> &mut Option<&'a mut i32> {
        &mut self.f_hat
    }

    fn execute(&self) {}
}

fn main() {
    let mut f_hat = 42;

    let mut plan = Plan::default();
    *plan.f_hat() = Some(&mut f_hat);
    plan.execute();
    **plan.f_hat().as_mut().unwrap() = 3;
    plan.execute();
}

也可以看看:


推荐阅读