Impl-tools

Test Status Latest version API Minimum rustc version

A set of helper macros

Macros

Autoimpl

#[autoimpl] is a partial replacement for #[derive], supporting:

#[autoimpl] may also be used on trait definitions to impl for specified types supporting Deref.

Unlike alternatives, #[autoimpl] has minimal and intuitive syntax.

```rust use impl_tools::autoimpl; use std::fmt::Debug;

// Impl Animal for Box where T: Animal + ?Sized

[autoimpl(for Box)]

trait Animal { fn numberoflegs(&self) -> u32; }

// Impl Debug for Named omitting field animal from output

[autoimpl(Debug ignore self.animal where T: Debug)]

// Impl Deref and DerefMut to field animal for Named

[autoimpl(Deref, DerefMut using self.animal)]

struct Named { name: T, animal: A, }

fn main() { struct Fish; impl Animal for Fish { fn numberoflegs(&self) -> u32 { 0 } }

let my_fish = Named {
    name: "Nemo",
    animal: Box::new(Fish),
};

assert_eq!(
    format!("{my_fish:?} has {} legs!", my_fish.number_of_legs()),
    r#"Named { name: "Nemo", .. } has 0 legs!"#
);

} ```

New-type wrappers

A combination of Deref on the new-type and trait-reimplementation on the trait allows succinct new-type patterns:

```rust use impl_tools::autoimpl; use std::sync::Arc;

// Impl Foo for &T, &mut T and Arc

[autoimpl(for &T, &mut T, Arc)]

// Optional: impl Foo for NewFoo (requires NewFoo: Deref)

[autoimpl(for NewFoo)]

pub trait Foo { fn success(&self) -> bool; }

// Impl Deref and DerefMut to a Target which itself supports Foo

[autoimpl(Deref, DerefMut using self.0)]

pub struct NewFoo(T);

// Impl Deref and DerefMut to a Target which itself supports Foo

[autoimpl(Deref, DerefMut using self.0)]

pub struct ArcDynFoo(Arc);

[test]

fn testfoonewtypes() { struct Success; impl Foo for Success { fn success(&self) -> bool { true } }

// We can now directly call Foo's methods on the wrapper:
assert!(NewFoo(Success).success());
assert!(ArcDynFoo(Arc::new(Success)).success());

} ```

See tests/newtype.rs for more variants of this pattern.

Impl Default

#[impl_default] implements std::default::Default:

```rust

[impltools::impldefault(Tree::Ash)]

enum Tree { Ash, Beech, Birch, Willow }

impltools::implscope! { #[impldefault] struct Copse { treetype: Tree, number: u32 = 7, } } ```

Note: #[impl_default] is matched within an impl_scope! regardless of imports.

Impl Scope

impl_scope! is a function-like macro used to define a type plus its implementations. It supports impl Self syntax:

```rust use std::fmt::Display;

impltools::implscope! { /// I don't know why this exists pub struct NamedThing { name: T, func: F, }

// Repeats generic parameters of type
impl Self {
    fn format_name(&self) -> String {
        format!("{}", self.name)
    }
}

// Merges generic parameters of type
impl<O> Self where F: Fn(&str) -> O {
    fn invoke(&self) -> O {
        (self.func)(&self.format_name())
    }
}

} ```

Caveat: rustfmt won't currently touch the contents. Hopefully that can be fixed!

Impl Anon

impl_anon! is a function-like macro to construct a single-use struct with custom implementations (similar: RFC#2604).

Example: rust use std::fmt; fn main() { let world = "world"; let says_hello_world = impl_tools::impl_anon! { struct(&'static str = world); impl fmt::Display for Self { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "hello {}", self.0) } } }; assert_eq!(format!("{}", says_hello_world), "hello world"); }

Extensibility

Rust's #[derive] macro is extensible via #[proc_macro_derive] in a proc-macro crate. Our macros cannot be extended in the same way, but they can be extended via a new front-end:

  1. Create a copy of the impl-tools crate to create a new "front-end" (proc-macro crate). This crate is contains only a little code over the [impl-tools-lib] crate.
  2. To extend #[autoimpl], write an impl of [ImplTrait] and add it to the attribute's definition. To extend impl_scope!, write an impl of [ScopeAttr] and add it to the macro's definition.
  3. Depend on your new front end crate instead of impl-tools.

For an example of this approach, see kas-macros.

Supported Rust Versions

The MSRV is 1.58.0.

When using a sufficiently recent compiler version (presumably 1.65.0), generic associated types are supported (only applicable to #[autoimpl] on trait definitions using GATs).

Alternatives

Derive alternatives

Both Educe and Derivative have similar functionality: the ability to implement standard traits with more flexibility than libstd's #[derive].

In comparison, impl-tools' #[autoimpl] has cleaner syntax but is less flexible: ```rust,ignore

[derive(Derivative)]

[derivative(PartialEq, Eq)]

struct Foo { foo: S, #[derivative(PartialEq="ignore")] bar: u8, #[derivative(PartialEq(bound=""), Eq(bound=""))] ptr: *const T, }

[derive(Educe)]

[educe(PartialEq(bound = "S: PartialEq"), Eq(bound = "S: Eq"))]

struct Foo { foo: S, #[educe(PartialEq(ignore))] bar: u8, ptr: *const T, }

// impl-tools:

[autoimpl(PartialEq, Eq ignore self.bar where S: trait)]

struct Foo { foo: S, bar: u8, ptr: *const T, } ```

Note: #[derive] and Derivative add bounds like S: PartialEq, T: PartialEq on generic parameters by default; Educe and impl-tools do not.

Derive extensions

derive_more isn't exactly an "alternative", simply supporting #[derive] for more standard traits such as Add and From. This is not (currently) supported by #[autoimpl] (or, to my knowledge, any alternative).

autoimpl allows implementing a trait for reference types (&, &mut, Box, Rc, Arc) as well as function types. The former (reference types) is supported by #[autoimpl] (and is slightly more general): ```rust,ignore // autoimpl:

[auto_impl(&, Box)]

trait Foo { fn foo(&self); }

// impl-tools:

[autoimpl(for &T, Box)]

trait Foo { fn foo(&self); } ```

Copyright and Licence

The COPYRIGHT file includes a list of contributors who claim copyright on this project. This list may be incomplete; new contributors may optionally add themselves to this list.

The impl-tools library is published under the terms of the Apache License, Version 2.0. You may obtain a copy of this licence from the LICENSE file or on the following webpage: https://www.apache.org/licenses/LICENSE-2.0