Mutable/immutable borrow question


#1

I’m trying to understand why there is a compile error in this code.

#[derive(Debug, Default)]
struct Holding {
    id: String,
}

#[derive(Debug, Default)]
struct Account {
    id: String,
    holdings: Vec<Holding>,
}

#[derive(Debug, Default)]
struct BS {
    id: String,
    accounts: Vec<Account>,
}

//////////////////////////////////////////////////
#[derive(Debug)]
struct HoldingWrapper<'a> {
    holding: &'a Holding,
    val: u32
}

#[derive(Debug)]
struct AccountWrapper<'a> {
    account: &'a Account,
    holding_wrappers: Vec<HoldingWrapper<'a>>,
}

#[derive(Debug)]
struct BSWrapper<'a> {
    account_wrappers: Vec<AccountWrapper<'a>>,
}

impl<'a> BSWrapper<'a> {
    fn from_bs(bs: &'a BS) -> BSWrapper<'a> {
        let mut account_wrappers = Vec::with_capacity(bs.accounts.len());
        for account in &bs.accounts {
            let mut holding_wrappers = Vec::with_capacity(account.holdings.len());
            for holding in &account.holdings {
                holding_wrappers.push(HoldingWrapper{holding, val: 0});
            }
            account_wrappers.push(AccountWrapper { account, holding_wrappers })
        }
        BSWrapper {
            account_wrappers
        }
    }
    fn get_holding_wrapper_mut(account_wrappers: &'a mut Vec<AccountWrapper<'a>>,
      account_index: usize, holding_index: usize) 
      -> Option<& 'a mut HoldingWrapper<'a>> 
      {
          if let Some(account_wrapper) = account_wrappers.get_mut(account_index) {
              return account_wrapper.holding_wrappers.get_mut(holding_index)
          }
          None
      }
}
//////////////////////////////////////////////////

fn main() {
    let bs = BS {
        id: "BS1".into(),
        accounts: vec![Account {
            id: "Acct1".into(),
            holdings: vec![Holding { id: "H1".into() }, Holding { id: "H2".into() }],
        }],
    };
    
    let mut bs_wrapper = BSWrapper::from_bs(& bs);

    println!("{:#?}", &bs_wrapper);
    
    {
        let account_wrappers = & mut bs_wrapper.account_wrappers;
        let holding = BSWrapper::get_holding_wrapper_mut(account_wrappers, 0, 0).unwrap();
        holding.val = 42;
    }
    
    println!("{:#?}", &bs_wrapper);
}

I don’t understand why the borrow inside the block is extended to the end of the function. This code is in the playground here: https://play.rust-lang.org/?gist=78fc39495bcaf963d557ef89cc7a289d&version=stable

Any explanations appreciated.


#2

When a function borrows &'a mut with the same lifetime of the object itself, then you’re making it legal for the function to hold that reference for the remaining lifetime of that object! The compiler doesn’t do inter-procedural analysis to see if you actually did that; it just assumes the worst of what the function signature allows.

Instead, you can use a distinct (shorter) lifetime for the mutable borrow:

    fn get_holding_wrapper_mut<'b>(account_wrappers: &'b mut Vec<AccountWrapper<'a>>,
      account_index: usize, holding_index: usize) 
      -> Option<&'b mut HoldingWrapper<'a>> 

#3

Perfect explanation and it worked, thanks!