Yesterday I was trying to produce something that resembles a dependency injection container in Rust. I came to the point where I was sick and tired of managing lifetimes. I needed to control the lifetime of object myself - a simple pointer!
Preferably, not too pointy, so I won’t stab myself. Still very unsafe. But probably it would be possible to wrap this logic into something so it is safe to use externally.
The official intro in writing unsafe code was a good eye-opener, however it warned me just too many times to avoid mem::transmute, so I just had to. The mem::transmute doc was not exactly helpful, but with a help of print statement and other code references I think I figured it out.
Turns out that if you pass a value into mem::transmute, it will eat ownership of it. If you transmute it into something stupid like *mut u8 (read: void pointer), it is now your responsibility to manage the result.
Well, the above example compiles and runs, but the result is not really visible. Let’s use something more debugable instead of int - a value that can print itself when it is created and destroyed:
And modify the code:
Oh, wait… We expected no destruction…. Wel, duh, of course, because we transmuted the reference instead of actual value. If instead we wrap the value in a Box…
Excellent, not destroyed! The box got eaten by transmute - nothing owns the val no more. We have a memory leak! How nice.
Of course, the same is going to work backwards too:
Turns out that when we get back the value from transmute, it is reintroduced in the current scope, and rust is generating a destructor for it as if we did the following:
This is excellent for taking control of the pointer and accidentally shooting yourself in the foot, because you can convert it back to anything and get varied garbage or crash horribly.
I will try to give an example (maybe a bit contrived) of something very simple that could use this mechanism.
Let’s make an object that might store a value of arbitrary type and call it MaybeValue. It will have a mighty pointer inside, as well as the TypeId of stored value:
We can use generics to create this value from any kind of type, box it and store it’s pointer as well as type.
And we can also add method to take away the contained value, called rob (to emphasize what it does):
Great! Should only rob once, even if we try to get value twice:
So, it is safe now, right? Well, what happens to value if no one robs it?
Ooops! MaybeValue has no idea how to correctly destroy this pointer. We could implement Drop, but it won’t know anything about the type, because it was only available in new…
What if instead we used a closure that knows how to drop it and is initialized inside new?
To destroy it, we simply “untransmute” it back from pointer to real type so that rust can automatically dispose of it.
And this indeed works out allright:
I like how Rust allows using powerful language constructs to wrap low level implementation, and keep this implementation safely locked. I truly believe that Rust is going to be amazing tool for building both high and low level applications.
Disclaimer: I am newbie in rust, use this as an example only and do not trust my code!