This article was written in collaboration with Neil Mitchell, a Software Engineer in the Developer Infrastructure organization at Facebook.
The Rust library Gazebo contains a collection of well-tested Rust utilities in the form of standalone modules. In this series of blog posts, we will cover some of the modules that make up the Gazebo library. In today’s blog, we will cover the module Cast. This blog is a part of our Rust Nibbles series, where we go over the various Rust libraries we have open-sourced to learn more about what motivated their creation and how one can use them.
One of the appeals of Rust is that it tries its best to help you avoid dangerous mistakes - but there are still unsafe functions available. Rust has a function called transmute, which converts one type to another. For example, you can write:
let y = unsafe { transmute(x) };
This fragment converts from a u64 to a [u8; 8]; or from a String to a Vec<u8>; or from a &str to an &(). Alternatively stated, the above code might do anything, and for a function like transmute, you probably really care about precisely what it does. This realisation led us to introduce the Gazebo cast module, which provides more restrictive alternatives.
The first entry in the cast module is the transmute! macro. Morally, transmute is a function from an input type to an output type, which takes a value of the input type. While usually Rust inferring types for us is useful, for transmute it is often harmful. With transmute! as a macro we can take 3 arguments, two of them types, so can write:
let y = unsafe { transmute!(u64, [u8; 8], x) };
It’s no less unsafe, but it’s much more explicit. Alternatively, using the existing transmute function you can write:
let y = unsafe { transmute::<u64, [u8; 8]>(x) };
And we are hopeful that one day Clippy can be extended to suggest giving the types explicitly.
While transmute can do many conversions, in some cases there are preferred ways of converting between types. For example, if you try using transmute to convert from &Foo to &Bar, clippy will suggest &*(x as *const Foo as *const Bar). That’s quite verbose. But more importantly, it obscures what you are trying to do. Therefore, Gazebo provides cast::ptr(x) which wraps up this conversion for you. The cast module provides 5 specific conversions we’ve found useful:
pub fn ptr_to_usize<T: ?Sized>(x: &T) -> usize; pub unsafe fn usize_to_ptr<'a, T>(x: usize) -> &'a T; pub unsafe fn ptr<From, To>(x: &From) -> &To; pub unsafe fn ptr_mut<From, To>(x: &mut From) -> &mut To; pub unsafe fn ptr_lifetime<'a, 'b, T: ?Sized>(x: &'a T) -> &'b T;
These are unsafe, all could be implemented as transmute, but are implemented as pointer conversions as that is the recommended form. The hope is these conversions make the intent clearer.
The transmute function can be used to do lots of unsafe things. But sometimes, after a long day coding, do you wonder if transmute is a little too safe? In particular, transmute checks that the input and output types have the same size. That’s great for concrete type parameters, but means that you can’t convert a Vec<T> to a String without having the T specified at compile time. That restriction, while usually a very good thing, can sometimes be harmful - so we provide transmute_unchecked for those cases. It’s exactly like transmute, but without a compile-time check of equal sizes. If the sizes aren’t equal things are highly likely to segfault, and in fact, if any of the other transmute invariants don’t hold it is undefined behaviour. But having that more powerful function is sometimes necessary.
We hope that this blog helps you understand the Cast module, how to use it and gives you good insight into what it does. Look out for our next blog in this series, where we discuss all the remaining little bits of Gazebo.
Be sure to check out our previous blogs in the Gazebo series to learn more about the various features the Gazebo library has to offer -
Gazebo - Prelude
Gazebo - Dupe
Gazebo - Variants
Gazebo - AnyLifetime
Gazebo - Comparisons
We at Facebook believe that Rust is an outstanding language that shines in critical issues such as memory safety, performance and reliability. We joined the Rust Foundation to help contribute towards the growth, advancement and adoption of Rust, and towards sustainable development of open source technologies and developer communities across the world.
This blog is a part of our Rust Nibbles series, where we go over the various Rust libraries we have open-sourced to learn more about what motivated their creation and how one can use them. We hope that this series helps you create amazing projects by using these libraries and encourages you to try them out.
To learn more about Facebook Open Source, visit our open source site, subscribe to our YouTube channel, or follow us on Twitter and Facebook.
Sign up for monthly updates from Meta for Developers.