Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Override Drop trait in a third party library

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:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

cannot move out of type CustomHistogramTimer, which implements the Drop trait
move occurs because self.0 has type HistogramTimer, which does not implement the Copy trait

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()
    }
}
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading