I've recently been asked what was an interesting bug I've run into during a project, and after discarding some boring ones (openssl failing on macos, correct utf8 parsing, 4 variables to set the same limit), I've remembered there was a more fun one.

I've written a wrapper over std::collections::HashMap to make entries expirying, aka if I insert a value at t0 = 0s that expires in 5s:

em.insert("key", "value", Duration::from_secs(5))

and then I try to get that value at t1 = 6s:

em.get("key")

It will be None, instead of Some("value").

Now, before you see the snippet, let the record show I know unwraps are bad.
You should use them carefully, and unwrapping a .get() is pretty bad.

But this wasn't that important, and I was quite sure those gets would never fail.

Buuuuut.... I might have forgot I was using those expiring maps, instead of the standard ones - which made otherwise non-fallible operations possible fallible.

 1let map: MutexGuard<ExpiringMap> = ml.lock().unwrap();
 2{
 3    let counter = map.get(&key).unwrap() + 1;
 4    if counter > allowed {
 5        map2.insert(key.clone(), duration);
 6        map.remove(&key);
 7        return false;
 8    }
 9    *map.get_mut(&key).unwrap() = counter;
10    return true;
11}
12// lock goes out of scope

So even if line 3's unwrap succeds, line 9 can panic.

The 2-11 scope was actually a function call, so it took opening two (2!) files to get the whole picture - why line 1 mutex, that no other thread modified, panicked in the inner scope.

While a lot of things here probably should be done quite differently, maybe, as I've mentioned above, probably don't unwrap gets.