A relatively common pattern that I employ is that I have a &mut self and I need to temporarily have both &mut self and &mut self.somefield. Usually the way I do it, is that I make the field a Option<T> and just self.somefield.take() and then self.somefield.put(v) it back.
However, that's kind of a lot of churn. I wish there was something nicer to accomplish it. I looked around and there's already detach - Rust which addresses exactly the thing I'm talking about but this macro API is far from ideal, IMO.
What I would ideally want is something like:
struct Foo
bar: Detach<String>,
... // stuff
}
impl Foo {
fn use_bar(&mut self) {
self.bar.detach().with(|s| { *s = format!("{} more with {}", s, self.some_mutable_stuff());});
}
}
or something. The detach() would be sort of a guard on the field. I thought about it, and I can't figure out how to make this API safe. The piece that I'm missing is somehow making the result of detach() be bound to exactly same lifetime as &mut self without actually mutably borrowing self. Runtime check would make sure a field can't be detached twice, and lifetime would make sure that self does not dissapear while s is used. Is there any technique to accomplish this?
Alternatively - does anyone have any idea how to make an API better then a macro?
The detach crate is just a macro to generate your Option approach with some utility to panic when you expected it to not be detached. Perhaps you know this, but the rest of your post made me think you're looking for something that doesn't alter the data structure?
If so, your example isn't workable as such because you would have two mutable reference paths to the same data at the same time. E.g. if you're not using Option or similar, how can you be sure some_mutable_stuff doesn't look at or clobber the detached field?
(The example is also simple enough to work around I'm not convinced I understand your use case.)
Oftentimes. But this generally requires twisting code and/or splitting structs into sub-structs etc. for the sole purpose of satisfying borrow checker. In a lot of cases take + put is much simpler and generally is more flexible, at the cost of Option and runtime checks it brings. What I'm looking for is just making take + put more ergonomic.
One issue with this API that I can anticipate is that a &mut self allows one to replace the entire Detach<T> field using safe code, mutating the T without obtaining a &mut T through the Detach API, bypassing its runtime check.
Right, so, you're okay altering the data structure.
The only fully general solution I can think of is making detachable fields Arc<RefCell> (or Rc<RefCell>) so you can leverage shared ownership to maintain a path to the field which is independent of &mut self (by detaching cloning the Arc). Then it won't matter if some other &mut self method clobbers your field; it won't be dropped because you're maintaining ownership elsewhere.
(Won't matter in terms of Rust's safety guarantees; probably still a bug.)
I'm think the mere existence of a &mut self for the struct to which you also have a shared reference of one of its fields would be UB due to aliasing rules, no?
If the field borrow is derived from the self borrow in a way the language recognizes it's fine (and using the self borrow kills the field borrow), but otherwise it's UB.
Indeed. I think that's OK. The only other approach I can think of is using some form of static magic-counter, incremented on every instance construction and checked before attempting the drop.