A set of helper macros
#[autoimpl] is a partial replacement for #[derive], supporting:
- Explicit
whereclause on generic parameters - No implicit bounds on generic parameters beyond those required by the type
- Traits like
Derefbyusinga named field - Traits like
Debugmayignorenamed fields
#[autoimpl] may also be used on trait definitions to impl for specified types
supporting Deref.
Unlike alternatives, #[autoimpl] has minimal and intuitive syntax.
use impl_tools::autoimpl;use std::fmt::Debug;// Impl Animal for Box<T> where T: Animal + ?Sized#[autoimpl(for<T:trait + ?Sized> Box<T>)]traitAnimal{fnnumber_of_legs(&self) -> u32;}// Impl Debug for Named<T, A: Animal> omitting field animal from output#[autoimpl(Debug ignore self.animal whereT:Debug)]// Impl Deref and DerefMut to field animal for Named<T, A: Animal>#[autoimpl(Deref,DerefMut using self.animal)]structNamed<T,A:Animal>{name:T,animal:A,}fnmain(){structFish;implAnimalforFish{fnnumber_of_legs(&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!"#);}A combination of Deref on the new-type and trait-reimplementation on the
trait allows succinct new-type patterns:
use impl_tools::autoimpl;use std::sync::Arc;// Impl Foo for &T, &mut T and Arc<T>#[autoimpl(for<T:trait + ?Sized> &T,&mutT,Arc<T>)]// Optional: impl Foo for NewFoo (requires NewFoo: Deref<Target = T>)#[autoimpl(for<T:trait> NewFoo<T>)]pubtraitFoo{fnsuccess(&self) -> bool;}// Impl Deref and DerefMut to a Target which itself supports Foo#[autoimpl(Deref<Target = T>,DerefMut using self.0)]pubstructNewFoo<T:Foo>(T);// Impl Deref and DerefMut to a Target which itself supports Foo#[autoimpl(Deref<Target = dyn Foo>,DerefMut using self.0)]pubstructArcDynFoo(Arc<dynFoo>);#[test]fntest_foo_newtypes(){structSuccess;implFooforSuccess{fnsuccess(&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] implements std::default::Default:
#[impl_tools::impl_default(Tree::Ash)]enumTree{Ash,Beech,Birch,Willow}
impl_tools::impl_scope! {
#[impl_default]structCopse{
tree_type:Tree,
number:u32 = 7,}}Note: #[impl_default] is matched within an impl_scope! regardless of imports.
Sometimes a trait is used only (or primarily) to provide an interface over a single object. In such cases, writing out the method prototypes twice (in both the trait and its implementation) should be unnecessary.
Example:
#[impl_tools::split_impl(forstr)]traitGreet{/// Introduce yourselffngreet(&self){println!("Hello world, I am {self}!");}}fnmain(){"Ferris".greet();}#[impl_self] provides impl Self syntax, avoiding the
need to repeat generics when writing impls on a local type definition.
This supersedes impl_scope! (except regarding macro@impl_default).
use std::fmt::Display;#[impl_tools::impl_self]modNamedThing{/// I don't know why this existspubstructNamedThing<T:Display,F>{name:T,func:F,}// Repeats generic parameters of typeimplSelf{fnformat_name(&self) -> String{format!("{}",self.name)}}// Merges generic parameters of typeimpl<O>SelfwhereF:Fn(&str) -> O{fninvoke(&self) -> O{(self.func)(&self.format_name())}}}Note that struct NamedThing is defined directly within the outer namespace,
not within the mod NamedThing. This is a hack required to ensure the contents
use valid Rust syntax and are thus formattable using cargo fmt.
impl_anon! is a function-like macro to construct a single-use struct with
custom implementations (similar: RFC#2604).
Example:
use std::fmt;fnmain(){let world = "world";let says_hello_world = impl_tools::impl_anon! {struct(&'staticstr = world);impl fmt::DisplayforSelf{fn fmt(&self, f:&mut fmt::Formatter) -> fmt::Result{
write!(f,"hello {}",self.0)}}};assert_eq!(format!("{}", says_hello_world),"hello world");}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:
- Create a copy of the
impl-toolscrate to create a new "front-end" (proc-macrocrate). This crate is contains only a little code over theimpl-tools-libcrate. - To extend
#[autoimpl], write an impl ofImplTraitand add it to the attribute's definition. To extend#[impl_self], write an impl ofScopeAttrand add it to the macro's definition. - Depend on your new front end crate instead of
impl-tools.
For an example of this approach, see kas-macros.
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:
#[derive(Derivative)]#[derivative(PartialEq,Eq)]structFoo<S,T: ?Sized>{foo:S,#[derivative(PartialEq="ignore")]bar:u8,#[derivative(PartialEq(bound=""),Eq(bound=""))]ptr:*constT,}#[derive(Educe)]#[educe(PartialEq(bound = "S: PartialEq"),Eq(bound = "S: Eq"))]structFoo<S,T: ?Sized>{foo:S,#[educe(PartialEq(ignore))]bar:u8,ptr:*constT,}// impl-tools:#[autoimpl(PartialEq,Eq ignore self.bar whereS:trait)]structFoo<S,T: ?Sized>{foo:S,bar:u8,ptr:*constT,}Note: #[derive] and Derivative add bounds like S: PartialEq, T: PartialEq on generic parameters by default; Educe and impl-tools do not.
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).
auto_impl 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):
// auto_impl:#[auto_impl(&,Box)]traitFoo{fnfoo(&self);}// impl-tools:#[autoimpl(for<T:trait + ?Sized> &T,Box<T>)]traitFoo{fnfoo(&self);}derive-where is a variant of the
standard #[derive] macro supporting custom generic bounds.
(This offers a subset of the functionality of #[autoimpl]).
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