Reputation: 1858
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.
Upvotes: -1
Views: 268
Reputation: 26
There is indeed a simple workaground other than unsafe
, though you need to reconstruct a new NfftPlan
.
fn releasedata(self)->NfftPlan {
NfftPlan{
f_hat:None,
x:self.x,
//...
}
}
And call it like this:
let mut f_hat = vec![1,2,3,4];
let mut plan = NfftPlan::new();
plan.set_f_hat(&f_hat);
plan.execute();
plan = plan.releasedata();
//Now the compiler forgets that plan would hold an borrow to f_hat
f_hat[0] = 3; // Change f_hat to a new value
plan.execute(); //New computation
If you are looking for a solution that can also avoid reconstructing, I am seeking for that solution too.
Upvotes: 0
Reputation: 431779
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:
Upvotes: 2