Working with exclusive borrowing and shared borrowing

How should I refactor my code to make it work with the borrow checker?

Either left or right is borrowed exclusively as ret and later the in the for loop, ret is mutated and also requiring the access of left and right. I understand the borrowing rules but I just couldn't figure out how to make it work.

// playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=892b27995aa72045a1e7c607fbe675dd

#![allow(unused)]
use std::cmp::min;

struct Beading {
    thickness: i64,
    widths: Vec<i64>,
    locations: Vec<usize>,
}

fn interpolate<'a>(left: &'a mut Beading, ratio: f64, right: &'a mut Beading) -> &'a Beading {
    let left_len = left.widths.len();
    let right_len = right.widths.len();
    
    // exclusive borrowing
    let ret: &mut Beading = if left.thickness > right.thickness {
        left
    } else {
        right
    };
    
    // problems
    for idx in 0..min(left_len, right_len) {
        if left.widths[idx] == 0 || right.widths[idx] == 0 {
            ret.widths[idx] = todo!();
        } else {
            ret.widths[idx] = todo!();
        }
        
        ret.locations = todo!();
    }
    ret
}

   Compiling playground v0.0.1 (/playground)
error[E0502]: cannot borrow `left.widths` as immutable because it is also borrowed as mutable
  --> src/lib.rs:23:12
   |
10 | fn interpolate<'a>(left: &'a mut Beading, ratio: f64, right: &'a mut Beading) -> &'a Beading {
   |                -- lifetime `'a` defined here
...
16 |         left
   |         ---- mutable borrow occurs here
...
23 |         if left.widths[idx] == 0 || right.widths[idx] == 0 {
   |            ^^^^^^^^^^^ immutable borrow occurs here
...
31 |     ret
   |     --- returning this value requires that `*left` is borrowed for `'a`

error[E0502]: cannot borrow `right.widths` as immutable because it is also borrowed as mutable
  --> src/lib.rs:23:37
   |
10 | fn interpolate<'a>(left: &'a mut Beading, ratio: f64, right: &'a mut Beading) -> &'a Beading {
   |                -- lifetime `'a` defined here
...
18 |         right
   |         ----- mutable borrow occurs here
...
23 |         if left.widths[idx] == 0 || right.widths[idx] == 0 {
   |                                     ^^^^^^^^^^^^ immutable borrow occurs here
...
31 |     ret
   |     --- returning this value requires that `*right` is borrowed for `'a`

Here, I put left and right in an array, then change ret to be an index into said array. Then, I modified all accesses to go through sides.

fn interpolate<'a>(left: &'a mut Beading, ratio: f64, right: &'a mut Beading) -> &'a Beading {
    let left_len = left.widths.len();
    let right_len = right.widths.len();

    let ret = if left.thickness > right.thickness { 0 } else { 1 };

    let mut sides = [left, right];

    // problems
    for idx in 0..min(left_len, right_len) {
        // cond and sides_ret are just to avoid doing the sides[ret] lookup more than once.
        // Probably not especially helpful.
        let cond = sides[0].widths[idx] == 0 || sides[1].widths[idx] == 0;

        let sides_ret = &mut sides[ret];
        if cond {
            sides_ret.widths[idx] = todo!();
        } else {
            sides_ret.widths[idx] = todo!();
        }

        sides_ret.locations = todo!();
    }

    sides[ret]
}
1 Like

From the OP, it doesn't look like you need to care which was originally left or right, and it doesn't look like you need indexing either.

fn interpolate<'a>(left: &'a mut Beading, ratio: f64, right: &'a mut Beading) -> &'a Beading {
    let (thick, thin) = match left.thickness > right.thickness {
        true => (left, right),
        false => (right, left)
    };

    for (ret, other) in thick.widths.iter_mut().zip(thin.widths.iter_mut()) {
        if *ret == 0 || *other == 0 {
            *ret = todo!();
        } else {
            *ret = todo!();
        }
        
        thick.locations = todo!();
    }
    
    thick
}
2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.