Make Key::get_st, Key::set_st, etc take an immutable reference
Motivating Example
While adding error handling to the Acculturation Workshop, I had some code that looked like this:
// crab, delta, horse are all KeyContexts
let (crab_t, delta_t, horse_t) = (toString(&crab[0]), toString(&delta[0]), toString(&horse[0]));
// confirm that timestamps match
if crab[1] != delta[1] || delta[1] != crab[1] {
println!("ACID fail: tDelta={}, tCrab={}, tHorse={}", crab_t, delta_t, horse_t);
process::exit(1);
}
let (crab_val, delta_val, horse_val): (i32, i32, i32) = (
toString(&crab.get().unwrap()).parse().unwrap(),
toString(&delta.get().unwrap()).parse().unwrap(),
toString(&horse.get().unwrap()).parse().unwrap(),
);
// confirm crab + horse == 0
if crab_val.wrapping_add(horse_val) != 0 {
println!("ACID fail: ^Crab({})={}; ^Horse({})={}", crab_t, crab_val, horse_t, horse_val);
process::exit(2);
}
This failed with a borrow error:
error[E0502]: cannot borrow `crab` as mutable because it is also borrowed as immutable
--> src/main.rs:42:23
|
32 | let (crab_t, delta_t, horse_t) = (toString(&crab[0]), toString(&delta[0]), toString(&horse[0]));
| ---- immutable borrow occurs here
...
42 | toString(&crab.get().unwrap()).parse().unwrap(),
| ^^^^^^^^^^ mutable borrow occurs here
...
48 | println!("ACID fail: ^Crab({})={}; ^Horse({})={}", crab_t, crab_val, horse_t, horse_val);
| ------ immutable borrow later used here
If crab.get() took &self instead of &mut self, there would have been no error.
Why this isn't currently possible
The .get_st and other methods on Key all call self.sync() before calling into the C functions. This call cannot be removed because the user could have modified self.buffers since the last call (i.e. the length, capacity, or buf_ptr could have changed). We can't call sync() inside of self.deref() because there's no way to run something after the buffer has been modified.
Possible solutions
-
Remove
impl DerefMut for Keyin favor ofIndexand a few other operations (#3 (closed)). Since these would take values instead of letting the user run arbitrary code, we could call.sync()upfront, removing the need to call.sync()in.get_st(), etc. -
Return a lock guard similar to MutexGuard which automatically calls
.sync()when it is dropped. I'm not sure if this is possible as it would need to have a&mut Keyto modify - it probably is but I haven't tried.
I think 1. is the better solution since it's a) simpler and b) closer to what we planned to do already.