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

  1. Remove impl DerefMut for Key in favor of Index and 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.

  2. 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 Key to 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.