Any way to borrow part of a struct?

I have a struct, with a method I call frequently, which looks like (simplified)

pub fn cell<'a>(&'a self, i: usize) -> &'a [usize] {
    &self.vals[self.starts[i]..self.ends[i]]
}

The problem I'm having is that I often want to call this and iterate through the resulting list while mutating another member of the struct, but that doesn't work, as I have already immutably borrowed this part of the struct.

At the moment I'm best solving this with a cell! macro, which just turns this function into a macro, so the compiler can see which member I'm accessing directly, but that feels fairly horrible.

I realize this is one of the "classic" borrow checker problems, but I was unsure if there were any up-to-date suggestions on how to best deal with this problem.

There's not, see also https://internals.rust-lang.org/t/pre-pre-rfc-opt-in-non-local-borrowck-analysis/8942 .

Well, actually you can put this cell together with the code that you borrow another member mutably, and return them together. You can also create a struct for the combination if you wish to do so.

Change the struct into a struct that has two fields, where one is a struct with vals, starts and ends and the other a struct with the rest.

Then change cell to be a method on the struct with vals, starts and ends and code that doesn't need those fields to be methods on the other struct; you may have also have functions that take an & to the first struct and an &mut to the second one and viceversa.

In addition to fixing the issue, this often improves the structure of the code since different concerns are now better separated.

Alternatively, if the division is dynamic or you can't change the struct, you can also define structs made of references to a subset of members of the struct and put the methods on those.

3 Likes

I'll give this a try. One thing which worried me (but might not be too hard to fix) is the data in this class should stay together as one member, it shouldn't be possible to reassign only half the struct.

I’d recommend destructuring, passing the pieces you want to modify, then restructuring.

Of course this doesn’t offer the guarantee you’re looking for that the pieces all stay together, but the only way you could really enforce that at compile time is to make all fields private, which makes the question moot.

Let us know what you ultimately do. It’s good to record these patterns and how people resolve them case by case.

Make the fields private, and don't reassign half the struct or otherwise break the invariants in the code that has access to them.

If you really want to enforce that, you can put the struct in a submodule that only provides accessors to the main module.