I have the following struct which represents a plan for a numeric computation:
pub struct NfftPlan<'a> {
x: Option<&'a [f64]>,
f_hat: Option<&'a [Complex64]>,
// ...
}
It has a set_f_hat
method:
pub fn set_f_hat(&mut self, f_hat: &'a [Complex64]) {
self.f_hat = Some(f_hat);
}
and an execute
method:
pub fn execute(&self) -> Vec<Complex64>
which uses f_hat
immutably.
I want to use this in the following way:
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
This fails because I cant mutate f_hat
while plan
still exists.
Is there a way for plan
to release the borrow to f_hat
which would allow me to mutate the f_hat
vector?
Something like this:
releasedata(&self) {
self.f_hat = None
} //Now the compiler forgets that plan would hold an borrow to f_hat
I know that Rust does not allow me to change the vector while a borrow to it exists, in this case via the f_hat
reference in the NfftPlan
struct.
I would like a way to tell the compiler to drop the reference to the vector in the NfftPlan
struct without dropping the entire struct.
How can I tell the compiler to release a borrow
You cannot, period. This isn't something you "tell" the compiler, the compiler knows all. You can only completely stop using the reference.
without dropping the entire struct
Dropping doesn't clear the borrow, only the borrow no longer being used does, which may happen from the drop.
f_hat[0] = 3; // Change f_hat to a new value plan.execute(); //New computation
This is exactly one of the types of code that Rust tries to prevent. It is not obvious at all that plan.execute()
should return a different value because some apparently unrelated value has changed.
I'd structure my types to reflect how they need to be used, creating throwaway values that can execute only once everything has been combined together. This means that the structure that borrows f_mut
is dropped as soon as it's done; note how this removes the Option
completely:
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();
}
See also: