Towards Overflower 1.0

23 October 2019

The overflower crate contains a helper library and procedural attribute macro to allow specifying how overflow should be handled. Since its inception, it has relied on specialization to change just the integer arithmetics while leaving the other operations basically unchanged.

Well, not really unchanged. See, procedural macros know very little about the types involved. This is why specialization is required: We simply implement the overflower helper traits that the macro uses to replace the std::ops traits in terms of the latter for all types we don’t want to change behavior for.

The first version of overflower was released in June 2016, which means it’s now more than three years old. Specialization is still unstable, so there’s no way to use it anywhere but nightly Rust. That got me thinking: “Could we put the specialization behind a feature and hack together a workaround for stable” and it ocurred to me that I could actually do this.

The sole problem we solve with specialization is that without it, the helper traits won’t be implemented for all types that may be used in the code. But perhaps if we offer a way to simply implement the trait, we could use let the user take care of it?

After a good deal of experimentation, I now have what I would call a working prototype. This version of overflower adds an impls! macro that you can use to implement the overflower traits for all your types that might implement any std::ops traits. It’s used like this:

// this will expand to all the trait impls overflower :: impls! ( MyType < 'a > : 'a ; all ); // or to be more explicit: overflower :: impls! ( MyType < 'a > : 'a ; add sub mul div rem shl shr neg add_assign sub_assign mul_assign div_assign rem_assign shl_assign shr_assign );

I thought about writing a proc_macro_derive for it, but that will likely cost even more compile time and the only benefit it offers is that you don’t have to write out your type twice. Also I couldn’t use a derive to implement the traits for std types ( Wrapping , NonZero* , String , Cow<'a, str> ).

There’s another change that will require you to slightly change your code: The #[overflow(_)] attribute and the helper traits are now directly importable from overflower , so the overflower_support crate is now an implementation detail you don’t need to care about any longer.

Also in order to keep the dependencies lean, I added a proc_macro default feature that you can disable if you just want to implement the overflow handling traits for your types. So for those cases, you can depend on overflower with overflower = { version = "0.9", default-features = false }; in your Cargo.toml ’s [dependencies] section.

If you do this, you’ll likely want overflower as an optional dependency, so you’d use: overflower = { version = "0.9", default-features = false, optional = true }; . Then in your code, you can use a #[cfg] to let users opt in to the trait implementations:

#[cfg(feature = "overflower" )] overflower :: impls! ( VeryInterestingType ; div );

Please keep in mind that those trait implementations only forward to the std::ops traits, they don’t handle overflow at all, regardless of any #[overflow] attribute.

The code is in the overflower-0.9 branch should you want to play with it.

Roadmap

I feel that overflower now works mostly the way I want it. Sure, some macros could probably be simplified, but apart from a little polish here and there, I see no big changes before we get that shiny 1.0.0. Onwards!

⚘