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.