-1

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.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Unlikus
  • 1,419
  • 10
  • 24
  • 3
    It's hard to answer your question because it doesn't include a [MRE]. We can't tell what crates (and their versions), types, traits, fields, etc. are present in the code. It would make it easier for us to help you if you try to reproduce your error on the [Rust Playground](https://play.rust-lang.org) if possible, otherwise in a brand new Cargo project, then [edit] your question to include the additional info. There are [Rust-specific MRE tips](//stackoverflow.com/tags/rust/info) you can use to reduce your original code for posting here. Thanks! – Shepmaster Aug 28 '19 at 14:25
  • 3
    Please paste the exact and entire error that you're getting — that will help us to understand what the problem is so we can help best. Sometimes trying to interpret an error message is tricky and it's actually a different part of the error message that's important. – Shepmaster Aug 28 '19 at 14:25
  • I know exactly why my code is not working. Rust does not allow me to change my vector, while there exists references to it. My question is how I can remove these references transparent for the compiler – Unlikus Aug 28 '19 at 14:40

1 Answers1

2

Explanation

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.

Solutions

Encode it in the type system

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 interior mutability and reference counting

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();
}

Recognize that the member is mutable

#[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:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • This solves my particular problem. But more general, I could need two values `x` and `f_hat` which require expensive precomputing. How would a struct or structs look like to represent this. When I change `x` I don't want to loose the precomputation from `f_hat` and the over way around. – Unlikus Aug 28 '19 at 15:20
  • 1
    @Unlikus follow the same pattern. Have a struct for `x` and whatever values are calculated from it, a struct for `f_hat` and whatever values are calculated from it, then a third struct that combines the two and calculates whatever values from both. – Shepmaster Aug 28 '19 at 15:24