Is there a way to get reference to a value contained in enum without `match/if let/let else` checking?

Consider the following code. In fn insert I set the value of the enum based on some conditions and then return a &mut to the contained value. The only way I could do this was using match (or perhaps if let / let else. However, I know that I have just set the enum in Part 1 so having a check again with match/if let/let else in Part 2 seems unnecessary. Is there a way to get reference to a value contained in enum without match/if let/let else checking? (also unsafe is ok).

pub enum Slot<T> {
    Empty,
    WasOccupied,
    IsOccupiedBy(T),
}

struct Entry<V> {
  ... // other struct members
  value: V,
}
...
struct MyStruct<V> {
  _data: Vec<Slot<Entry<V>>>,
  ...
}
...

impl<V> MyStruct<V> {
  //update existing value or insert new and return old value and mut ref to to new value
  fn insert(&mut self, v: V) -> Result<(Option<V>, &mut V), OutOfCapacityError>
    let mut old_val: Option<V> = None;

    // Part 1: Set a value
    match self._data[i] {
        Slot::IsOccupiedBy(ref mut entry) => {
            old_val = Some(mem::replace(&mut entry.value, value));
        }
        Slot::Empty | Slot::WasOccupied => {
            self._data[i] = Slot::IsOccupiedBy(Entry {
                ... // other struct members
                value,
            });
        }
    }

    //.... some more code that doesn't update/mutate self._data

    // Part 2: return the &mut to the contained value
    match self._data[i] { // At this point I already know self._data[i] has an Entry containing  value
        Slot::IsOccupiedBy(ref mut entry) => {    
            return Result::Ok((old_val, &mut entry.value))
        }
        _ => panic!("Unreachable!! Something went horribly wrong if I hit this line")   
    }
  }
}
1 Like

Not really. Though you can simplify/tidy things up a little bit:

return Ok(match self._data[i] {
    Slot::IsOccupiedBy(ref mut entry) => {
        old_val = Some(mem::replace(&mut entry.value, value));
        (old_val, &mut entry.value)
    }
    ref mut free => {
        *free = Slot::IsOccupiedBy(Entry { value, ... });
        // no way around this, as there's no way to tell 
        // where *exactly* in memory the `value` will end up 
        let Slot::IsOccupiedBy(Entry { value, .. }) = free 
            else { unreachable!("new entry just added") }
        (old_val, value)
    }
});
5 Likes

let-else-unreachable! is great for this. If you just set it on the previous line like that, the optimizer will be highly reliable in removing the check, and even if for some weird reason it didn't, the branch predictor will get it right every time.

6 Likes