I’m using prometheus histogram timer.
This struct implements Drop trait
impl Drop for HistogramTimer {
fn drop(&mut self) {
if !self.observed {
self.observe(true);
}
}
}
I need to override this with a noop implementation.
I tried this:
struct CustomHistogramTimer(HistogramTimer);
impl CustomHistogramTimer{
pub fn stop_and_record(self) -> f64 {
self.0.stop_and_record()
}
pub fn stop_and_discard(self) -> f64 {
self.0.stop_and_discard()
}
}
impl Drop for CustomHistogramTimer {
fn drop(&mut self) {
}
}
But this doesn’t work because I get:
cannot move out of type
CustomHistogramTimer, which implements theDroptrait
move occurs becauseself.0has typeHistogramTimer, which does not implement theCopytrait
I have no idea how to solve this.
>Solution :
The simplest solution might be to use ManuallyDrop and simply never drop the value.
struct CustomHistogramTimer(ManuallyDrop<HistogramTimer>);
Then you can remove your Drop implementation. When this type is dropped, the contained HistogramTimer will be forgotten (discarded without dropping it).
However, note that HistogramTimer contains a Histogram, which in turn contains an Arc to the histogram’s internal state. This means that not dropping a HistogramTimer will cause the histogram’s state to never be freed because the Arc‘s reference count will never become zero.
However, it looks like there is a solution. The documentation for HistogramTimer::stop_and_discard says (emphasis mine):
Observe and return timer duration (in seconds).
It returns a floating-point number of seconds elapsed since the timer started, without recording to any histogram.
Ok, great, but this is a consuming method. We can’t call it on a &mut HistogramTimer, which is all we can get in the drop code.
Thankfully, this is possible in combination with the ManuallyDrop approach. In fact, this particular case is one of the primary uses of ManuallyDrop. We have to use unsafe code to call ManuallyDrop::take, which gives us ownership of the inner T. This is safe so long as we never use the ManuallyDrop again, and since we’ll be doing this in drop code, that’s easy to ensure.
impl Drop for CustomHistogramTimer {
fn drop(&mut self) {
// SAFETY: The ManuallyDrop is being dropped and won't be used again.
let timer = unsafe { ManuallyDrop::take(&mut self.0) };
timer.stop_and_discard();
}
}
If you prefer an approach without using unsafe code, you can do this using Option. While we can easily prove the code will never panic, the compiler can’t, and so any code using the internal HistogramTimer will have a None check emitted.
struct CustomHistogramTimer(Option<HistogramTimer>);
impl Drop for CustomHistogramTimer {
fn drop(&mut self) {
self.0.take().unwrap().stop_and_discard();
}
}
impl CustomHistogramTimer {
pub fn stop_and_record(self) -> f64 {
self.0.unwrap().stop_and_record()
}
pub fn stop_and_discard(self) -> f64 {
self.0.unwrap().stop_and_discard()
}
}