You can use the consume_on_drop crate to do this. Full disclosure: I am the author of this crate.
extern crate consume_on_drop;
use consume_on_drop::{Consume, ConsumeOnDrop};
use std::ops::{Deref, DerefMut};
// We have a library given to us.
mod library_code {
pub struct T;
pub fn destroyT(_t: T) {
// code ommitted
}
impl T {
pub fn borrow_t(&self) {
// code ommitted
}
pub fn borrow_mut_t(&mut self) {
// code ommitted
}
}
}
use library_code::T;
// We can't implement consume for T itself, since T is defined
// in a library. We need a wrapper that implements Consume.
struct TConsume(T);
// The Consume trait defines what happens to our TConsume when
// we drop a value of type ConsumeOnDrop<TConsume>.
impl Consume for TConsume {
fn consume(self) {
library_code::destroyT(self.0)
}
}
// Dropping S will call destroyT on the underlying T.
// S is a zero-overhead wrapper around T.
pub struct S(ConsumeOnDrop<TConsume>);
impl Deref for S {
type Target = T;
fn deref(&self) -> &T {
&self.0.0
}
// We can now call s.borrow_t() where s: S
}
impl DerefMut for S {
fn deref_mut(&mut self) -> &mut T {
&mut self.0.0
}
// We can now call s.borrow_mut_t() where s: S
}
impl S {
/// Turn our S back into the underlying T
pub fn into_inner(self) -> T {
ConsumeOnDrop::into_inner(self.0).0
}
/// Turn a T into an S
pub fn new(val: T) -> Self {
Self(ConsumeOnDrop::new(TConsume(val)))
}
}
fn main() {
let mut s = S::new(T);
s.borrow_t();
s.borrow_mut_t();
drop(s); // Calls destroyT on underlying T
}
Alternately, if you don't care about giving the type S a name, you can use WithConsumer. In the code below, s is a zero-overhead wrapper around T which, when dropped, will have the consumer library_code::destroyT called on its underlying T.
You can also use a closure as a consumer, though closures may take up a nonzero amount of space (unlike a statically known function item like library_code::destroyT). This approach has the benefit of brevity, but you cannot actually name the type of s. It's possible that in the future, support for impl Trait will improve to the point that we can always use WithConsumer over ConsumeOnDrop.
extern crate consume_on_drop;
use consume_on_drop::WithConsumer;
mod library_code {
... // same library code as above
}
fn main() {
let mut s = WithConsumer::new(T, library_code::destroyT);
s.borrow_t();
s.borrow_mut_t();
drop(s); // calls destroyT on the underlying T
}