/blanket

A simple Rust macro to derive blanket implementations for your traits.

Primary LanguageRustMIT LicenseMIT

🧣 blanket Star me

A simple macro to derive blanket implementations for your traits.

Actions Codecov License Source Crate Documentation Changelog GitHub issues

🔍 Overview

The Rust standard library has plenty of traits, but they shine in how well they integrate with new types. Declare an implementation of std::io::Write for a type W, and you also get it for &mut W and Box<W>! This however translates into a lot of boilerplate code that can be hard to maintain, which is why many crates don't bother providing the same convenience implementations.

This is where blanket comes in! This crate helps you build the same kind of blanket implementations for your own traits with as least additional code as possible: in fact, this is as close as what a derive macro would look like for a trait item.

🔌 Usage

blanket exports a single eponymous attribute macro, which can be imported simply after the crate has been added to the Cargo.toml dependencies:

extern crate blanket;
use blanket::blanket;

#[blanket(derive(...))]

Use this macro attribute to derive a blanket implementation for a trait, provided the trait methods fit the constraints for that derive, such as only declaring methods with &self of &mut self as their receiver. The following derives are available:

Derive Impl block fn (&self) fn (&mut self) fn (self)
Ref impl<T: Trait + ?Sized> Trait for &T ✔️
Rc impl<T: Trait + ?Sized> Trait for Rc<T> ✔️
Arc impl<T: Trait + ?Sized> Trait for Arc<T> ✔️
Mut impl<T: Trait + ?Sized> Trait for &mut T ✔️ ✔️
Box impl<T: Trait> Trait for Box<T> ✔️ ✔️ ✔️

For instance, with our own version of std::fmt::Write, we can provide an implementation for Box<impl Write> and &mut impl Write:

extern crate blanket;
use blanket::blanket;

#[blanket(derive(Mut, Box))]
pub trait Write {
    fn write_str(&mut self, s: &str) -> std::fmt::Result;
    fn write_char(&mut self, c: char) -> std::fmt::Result {
         self.write_str(c.encode_utf8(&mut [0; 4]))
    }
}

Note that we can't derive Ref because the Write trait we declared expects mutable references, which we can't provide from an immutable reference. If we were to try, the compiler would warn us:

---- src/lib.rs - (line 55) stdout ----
error: cannot derive `Ref` for a trait declaring `&mut self` methods
 --> src/lib.rs:61:18
  |
8 |     fn write_str(&mut self, s: &str) -> std::fmt::Result;
  |                  ^^^^^^^^^

#[blanket(default = "...")]

blanket can delegate default implementations of trait methods to functions of another module. This can be useful for some traits such as visitors to provide a default behaviour as an external function, such as what syn::visit is doing.

The following example implements a very simple visitor trait for types able to process a &str char-by-char.

extern crate blanket;
use blanket::blanket;

#[blanket(default = "visitor")]
trait Visitor {
    fn visit_string(&self, s: &str);
    fn visit_char(&self, c: char);
}

mod visitor {
    use super::Visitor;

    pub fn visit_string<V: Visitor + ?Sized>(v: &V, s: &str) {
        for c in s.chars() {
            v.visit_char(c);
        }
    }

    pub fn visit_char<V: Visitor + ?Sized>(v: &V, c: char) {}
}

blanket will check that all methods are declared without a default block, and then create a default implementation for all of the declared methods, generating the following code:

trait Visitor {
    fn visit_string(&self, s: &str) {
      visitor::visit_string(self, s)
    }
    fn visit_char(&self, c: char) {
      visitor::visit_char(self, c)
    }
}

📝 To-Do

  • ✓ Delegation of default method to external functions.
  • ✓ Support for traits with generic arguments.
  • #[derive(Ref)]
  • #[derive(Mut)]
  • #[derive(Box)]
  • #[derive(Rc)]
  • #[derive(Arc)]
  • ✗ Update Box derive to allow unsized types if possible.
  • #[derive(Cow)]

🤝 Credits

blanket is developed and maintained by:

The following people contributed to the project:

📋 Changelog

This project adheres to Semantic Versioning and provides a changelog in the Keep a Changelog format.

📜 License

This library is provided under the open-source MIT license.