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
}