0

I'm facing a problem where I have a structure (called HomotopyOptimizer) which has a number of fields (some are borrowed). In HomotopyOptimizer there is a method with argument &self mut.

What I do is that I take some of the fields of my structure and either borrow them to other functions or move their ownership because I will not be needing them further.

Let me describe the situation with a graphic (I'll provide the code at the end):

Borrow checker issue

My HomotopyOptimizer has two fields: (i) one of type HomotopyProblem and (ii) one of type &Cache.

These, in turn are structures with certain fields. In particular, the HomotopyProblem field has three fields.

I need to construct a structure of type Problem using the fields of HomotopyProblem and by borrowing the field of type Cache from self, however, Rust complains that I cannot borrow it because self itself has been borrowed previously (or at least it seems so).

My main question is whether I can move the ownership of fields of a structure without moving or lending the structure itself.


Let me present my code in reverse order. First the function in which I get the compilation error:

impl<'cache_lifetime, 
     ParametricPenaltyFunctionType, 
     ParametricGradientType, 
     ConstraintType, ParametricCostType>
    HomotopyOptimizer<...same_as_above...>
where
    ParametricPenaltyFunctionType: Fn(&[f64], &[f64], &mut [f64]) -> Result<(), Error>,
    ParametricGradientType: Fn(&[f64], &[f64], &mut [f64]) -> Result<(), Error>,
    ParametricCostType: Fn(&[f64], &[f64], &mut f64) -> Result<(), Error>,
    ConstraintType: constraints::Constraint,
{

    pub fn solve(&'cache_lifetime mut self, u: &mut [f64]) {
        let p_ = [1., 2., 3.];

        let f_ = |u: &[f64], cost: &mut f64| -> Result<(), Error> {
            (self.homotopy_problem.parametric_cost)(u, &p_, cost)
        };
        let df_ = |u: &[f64], grad: &mut [f64]| -> Result<(), Error> {
            (self.homotopy_problem.parametric_gradient)(u, &p_, grad)
        };
        let problem_ = Problem::new(&self.homotopy_problem.constraints, df_, f_);

        let mut panoc_ = panoc::Optimizer::new(problem_, &mut self.panoc_cache);
        panoc_.solve(u);
        println!("u = {:#?}", u);
    }
}

I get the following error:

Compiling optimization_engine v0.3.1 (/home/chung/NetBeansProjects/RUST/optimization-engine)
error[E0502]: cannot borrow `self.panoc_cache` as mutable because it is also borrowed as immutable
  --> src/continuation/homotopy_optimizer.rs:83:63
   |
75 |         let f_ = |u: &[f64], cost: &mut f64| -> Result<(), Error> {
   |                  ------------------------------------------------ immutable borrow occurs here
76 |             (self.homotopy_problem.parametric_cost)(u, &p_, cost)
   |              ---- first borrow occurs due to use of `self` in closure
...
83 |         let mut panoc_ = panoc::Optimizer::new(problem_, &mut self.panoc_cache);
   |                          --------------------------           ^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
   |                          |
   |                          immutable borrow later used by call

error: aborting due to previous error

For more information about this error, try `rustc --explain E0502`.
error: Could not compile `optimization_engine`.
warning: build failed, waiting for other jobs to finish...
error[E0502]: cannot borrow `self.panoc_cache` as mutable because it is also borrowed as immutable
  --> src/continuation/homotopy_optimizer.rs:83:63
   |
75 |         let f_ = |u: &[f64], cost: &mut f64| -> Result<(), Error> {
   |                  ------------------------------------------------ immutable borrow occurs here
76 |             (self.homotopy_problem.parametric_cost)(u, &p_, cost)
   |              ---- first borrow occurs due to use of `self` in closure
...
83 |         let mut panoc_ = panoc::Optimizer::new(problem_, &mut self.panoc_cache);
   |                          --------------------------           ^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
   |                          |
   |                          immutable borrow later used by call

error: aborting due to previous error

For more information about this error, try `rustc --explain E0502`.
error: Could not compile `optimization_engine`.

For completeness: The definition of HomotopyOptimizer is

pub struct HomotopyOptimizer<
    'cache_lifetime,
    ParametricPenaltyFunctionType,
    ParametricGradientType,
    ConstraintType,
    ParametricCostType,
> where
    ParametricPenaltyFunctionType: Fn(&[f64], &[f64], &mut [f64]) -> Result<(), Error>,
    ParametricGradientType: Fn(&[f64], &[f64], &mut [f64]) -> Result<(), Error>,
    ParametricCostType: Fn(&[f64], &[f64], &mut f64) -> Result<(), Error>,
    ConstraintType: constraints::Constraint,
{
    homotopy_problem: HomotopyProblem<
        ParametricPenaltyFunctionType,
        ParametricGradientType,
        ConstraintType,
        ParametricCostType,
    >,
    panoc_cache: &'cache_lifetime mut panoc::Cache,
}

and its constructor is

pub fn new(
        homotopy_problem: HomotopyProblem<
            ParametricPenaltyFunctionType,
            ParametricGradientType,
            ConstraintType,
            ParametricCostType,
        >,
        panoc_cache: &'cache_lifetime mut panoc::PANOCCache,
    ) -> HomotopyOptimizer<
        ParametricPenaltyFunctionType,
        ParametricGradientType,
        ConstraintType,
        ParametricCostType,
    > {
        HomotopyOptimizer {
            homotopy_problem: homotopy_problem,
            panoc_cache: panoc_cache,
        }
    }

The definition of HomotopyProblem is

pub struct HomotopyProblem<
    ParametricPenaltyFunctionType,
    ParametricGradientType,
    ConstraintType,
    ParametricCostType,
> where
    ParametricPenaltyFunctionType: Fn(&[f64], &[f64], &mut [f64]) -> Result<(), Error>,
    ParametricGradientType: Fn(&[f64], &[f64], &mut [f64]) -> Result<(), Error>,
    ParametricCostType: Fn(&[f64], &[f64], &mut f64) -> Result<(), Error>,
    ConstraintType: constraints::Constraint,
{
    pub(crate) constraints: ConstraintType,
    pub(crate) parametric_gradient: ParametricGradientType,
    pub(crate) parametric_cost: ParametricCostType,
    pub(crate) penalty_function: ParametricPenaltyFunctionType,
    idx: Vec<usize>,
    from: Vec<f64>,
    to: Vec<f64>,
    transition_mode: Vec<i32>,
    num_parameters: usize,
}

and its constructor is

pub fn new(
        constraints: ConstraintType,
        parametric_gradient: ParametricGradientType,
        parametric_cost: ParametricCostType,
        penalty_function: ParametricPenaltyFunctionType,
        num_parameters: usize,
    ) -> HomotopyProblem<
        ParametricPenaltyFunctionType,
        ParametricGradientType,
        ConstraintType,
        ParametricCostType,
    > {
        HomotopyProblem {
            constraints: constraints,
            parametric_gradient: parametric_gradient,
            parametric_cost: parametric_cost,
            penalty_function: penalty_function,
            idx: Vec::new(),
            from: Vec::new(),
            to: Vec::new(),
            transition_mode: Vec::new(),
            num_parameters: num_parameters,
        }
    }
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Pantelis Sopasakis
  • 1,902
  • 5
  • 26
  • 45
  • 3
    Writing `self.homotopy_problem` in closure causes whole self being borrowed. By creating temporary value `let problem = &self.homotopy_problem;` and using it instead should solve the problem – Pavel Arnold May 23 '19 at 22:15
  • @PavelArnold Thank you very much for your answer! Indeed, this solved the problem, but I don't quite understand why. So you're saying that using `self.homotopy_problem` lends `self` to the closure? Shouldn't it just lend `homotopy_problem`? If you would like to write an answer, I will be able to upvote and accept it. – Pantelis Sopasakis May 23 '19 at 22:20
  • It's a bit hard to answer your question because it doesn't include a [MCVE]. **Minimize** your problem. It would make it much easier for us to help 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 MCVE tips](//stackoverflow.com/tags/rust/info) you can use to reduce your original code for posting here. Thanks! – Shepmaster May 24 '19 at 11:36
  • @Shepmaster Thank you, but it has already been answered by Pavel Arnold above. – Pantelis Sopasakis May 24 '19 at 12:04
  • My point is that if you had produced the minimal example to start with, you would have been *much* more likely to find one of the duplicates of this question, solving your own problem. – Shepmaster May 24 '19 at 12:09
  • @Shepmaster you're right; since this is a duplicate, should I delete it? – Pantelis Sopasakis May 24 '19 at 12:13
  • Duplicate questions are not bad; they serve as signposts to canonical answers. If someone else searches for the same terms that you used in your post, they will be redirected to the appropriate answer. – Shepmaster May 24 '19 at 12:16

0 Answers0