diff --git a/README.md b/README.md index ca7eb5d..475ea3a 100644 --- a/README.md +++ b/README.md @@ -1,216 +1,50 @@ -# `auto_impl` [![Join the chat at https://gitter.im/auto-impl-rs/Lobby](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/auto-impl-rs/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Build Status](https://travis-ci.org/auto-impl-rs/auto_impl.svg?branch=master)](https://travis-ci.org/auto-impl-rs/auto_impl) [![Crates.io](https://img.shields.io/crates/v/auto_impl.svg)](https://crates.io/crates/auto_impl) +# `auto_impl` [![Join the chat at https://gitter.im/auto-impl-rs/Lobby](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/auto-impl-rs/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Build Status](https://travis-ci.org/auto-impl-rs/auto_impl.svg?branch=master)](https://travis-ci.org/auto-impl-rs/auto_impl) [![Crates.io](https://img.shields.io/crates/v/auto_impl.svg)](https://crates.io/crates/auto_impl) [![docs](https://docs.rs/auto_impl/badge.svg)](https://docs.rs/auto_impl) -A simple compiler plugin for automatically implementing a trait for some common smart pointers and closures. +A proc-macro attribute for automatically implementing a trait for references, +some common smart pointers and closures. # Usage -This library requires the `nightly` channel. +This library requires Rust 1.30.0 or newer. -Add `auto_impl` to your `Cargo.toml`: - -``` -auto_impl = "*" -``` - -Reference in your crate root: +Add `auto_impl` to your `Cargo.toml` and just use it in your crate: ```rust -#![feature(proc_macro)] - -extern crate auto_impl; - +// In Rust 2015 you still need `extern crate auto_impl;` at your crate root use auto_impl::auto_impl; ``` -Add an `auto_impl` attribute to traits you want to automatically implement for wrapper types: - -```rust -#[auto_impl(&, Arc)] -pub trait OrderStoreFilter { - fn filter(&self, predicate: F) -> Result - where - F: Fn(&OrderData) -> bool; -} -``` - -Now anywhere that requires a `T: OrderStoreFilter` will also accept an `&T` or `Arc` where `T` implements `OrderStoreFilter`. - -## Specifics - -The following types are supported and can be freely combined: - -- [`Arc`](https://doc.rust-lang.org/std/sync/struct.Arc.html) -- [`Rc`](https://doc.rust-lang.org/std/rc/struct.Rc.html) -- [`Box`](https://doc.rust-lang.org/std/boxed/struct.Box.html) - -The following types are also supported, but require a blanket implementation (you can only have one of these per `trait`): - -- [`Fn`](https://doc.rust-lang.org/std/ops/trait.Fn.html) -- [`FnMut`](https://doc.rust-lang.org/std/ops/trait.FnMut.html) -- [`FnOnce`](https://doc.rust-lang.org/std/ops/trait.FnOnce.html) -- `&` -- `&mut` - -## Implement a trait for `Box` - -```rust -#[auto_impl(Box)] -trait MyTrait<'a, T> - where T: AsRef -{ - type Type1; - type Type2; - - fn execute1<'b>(&'a self, arg1: &'b T) -> Result; - fn execute2(&self) -> Self::Type2; - fn execute3(self) -> Self::Type1; - fn execute4() -> &'static str; -} -``` - -Will expand to: - -```rust -impl<'a, T, TAutoImpl> MyTrait<'a, T> for ::std::boxed::Box - where TAutoImpl: MyTrait<'a, T>, - T: AsRef -{ - type Type1 = TAutoImpl::Type1; - type Type2 = TAutoImpl::Type2; - - fn execute1<'b>(&'a self, arg1: &'b T) -> Result { - (**self).execute1(arg1) - } - - fn execute2(&self) -> Self::Type2 { - (**self).execute2() - } - - fn execute3(self) -> Self::Type1 { - (*self).execute3() - } - - fn execute4() -> &'static str { - TAutoImpl::execute4() - } -} -``` - -There are no restrictions on `auto_impl` for `Box`. - -## Implement a trait for a smart pointer - -Add the `#[auto_impl]` attribute to traits to automatically implement them for wrapper types: - -```rust -#[auto_impl(Arc, Rc)] -trait MyTrait<'a, T> - where T: AsRef -{ - type Type1; - type Type2; - - fn execute1<'b>(&'a self, arg1: &'b T) -> Result; - fn execute2(&self) -> Self::Type2; -} -``` - -Will expand to: - -```rust -impl<'a, T, TAutoImpl> MyTrait<'a, T> for ::std::sync::Arc - where TAutoImpl: MyTrait<'a, T>, - T: AsRef -{ - type Type1 = TAutoImpl::Type1; - type Type2 = TAutoImpl::Type2; - - fn execute1<'b>(&'a self, arg1: &'b T) -> Result { - (**self).execute1(arg1) - } - - fn execute2(&self) -> Self::Type2 { - (**self).execute2() - } -} - -impl<'a, T, TAutoImpl> MyTrait<'a, T> for ::std::rc::Rc - where TAutoImpl: MyTrait<'a, T>, - T: AsRef -{ - type Type1 = TAutoImpl::Type1; - type Type2 = TAutoImpl::Type2; - - fn execute1<'b>(&'a self, arg1: &'b T) -> Result { - (**self).execute1(arg1) - } - - fn execute2(&self) -> Self::Type2 { - (**self).execute2() - } -} -``` - -There are a few restrictions on `#[auto_impl]` for smart pointers. The trait must: - -- Only have methods that take `&self` or have no receiver (static) - -## Implement a trait for a closure +Add an `auto_impl` attribute to traits you want to automatically implement for wrapper types. Here is a small example: ```rust -#[auto_impl(Fn)] -trait MyTrait<'a, T> { - fn execute<'b>(&'a self, arg1: &'b T, arg2: &'static str) -> Result<(), String>; +// This will generate two additional impl blocks: one for `&T` and one +// for `Box` where `T: Foo`. +#[auto_impl(&, Box)] +trait Foo { + fn foo(&self); } -``` -Will expand to: - -```rust -impl<'a, T, TAutoImpl> MyTrait<'a, T> for TAutoImpl - where TAutoImpl: ::std::ops::Fn(&T, &'static str) -> Result<(), String> -{ - fn execute<'b>(&'a self, arg1: &'b T, arg1: &'static str) -> Result<(), String> { - self(arg1, arg2) - } +impl Foo for i32 { + fn foo(&self) {} } -``` -There are a few restrictions on `#[auto_impl]` for closures. The trait must: +fn requires_foo(_: impl Foo) {} -- Have a single method -- Have no associated types -- Have no non-static lifetimes in the return type -## Implement a trait for a borrowed reference - -```rust -#[auto_impl(&, &mut)] -trait MyTrait<'a, T> { - fn execute<'b>(&'a self, arg1: &'b T, arg2: &'static str) -> Result<(), String>; -} +requires_foo(0i32); // works: through the impl we defined above +requires_foo(&0i32); // works: through the generated impl +requires_foo(Box::new(0i32)); // works: through the generated impl ``` -Will expand to: +For more explanations, please see [**the documentation**](https://docs.rs/auto_impl) and for more examples, see [the examples folder](https://github.com/auto-impl-rs/auto_impl/tree/master/examples). -```rust -impl<'auto, 'a, T, TAutoImpl> MyTrait<'a, T> for &'auto TAutoImpl { - fn execute<'b>(&'a self, arg1: &'b T, arg1: &'static str) -> Result<(), String> { - (**self).execute(arg1, arg2) - } -} -impl<'auto, 'a, T, TAutoImpl> MyTrait<'a, T> for &'auto mut TAutoImpl { - fn execute<'b>(&'a self, arg1: &'b T, arg1: &'static str) -> Result<(), String> { - (**self).execute(arg1, arg2) - } -} -``` +--- -There are a few restrictions on `#[auto_impl]` for immutably borrowed references. The trait must: +## License -- Only have methods that take `&self` or have no receiver (static) +Licensed under MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT). -There are a few restrictions on `#[auto_impl]` for mutably borrowed references. The trait must: +### Contribution -- Only have methods that take `&self`, `&mut self` or have no receiver (static) +Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you shall be licensed as above, without any additional terms or conditions. diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..3f1078b --- /dev/null +++ b/examples/README.md @@ -0,0 +1,14 @@ +# Examples + +- `error_messages`: contains some incorrect code that showcases the error messages emitted by `auto_impl` +- **`greet_closure`**: simple example showing how to auto impl for `Fn` traits +- **`keep_default_for`**: shows how to use the `#[auto_impl(keep_default_for(...))]` attribute +- `names`: showcases how `auto_impl` chooses new ident names +- **`refs`**: shows how to auto impl for `&` and `Box` + + +**Note**: if you want to see what the generated impl blocks look like, use the execellent [`cargo expand`](https://github.com/dtolnay/cargo-expand): + +``` +$ cargo expand --example refs +``` diff --git a/src/lib.rs b/src/lib.rs index f1dede1..3c2fcb2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,228 @@ //! A proc-macro attribute for automatically implementing a trait for //! references, some common smart pointers and closures. - +//! +//! ## Simple example +//! +//! ``` +//! use auto_impl::auto_impl; +//! +//! // This will generate two additional impl blocks: one `for &T` and one +//! // `for Box` where `T: Foo`. +//! #[auto_impl(&, Box)] +//! trait Foo { +//! fn foo(&self); +//! } +//! +//! impl Foo for i32 { +//! fn foo(&self) {} +//! } +//! +//! fn requires_foo(_: impl Foo) {} +//! +//! +//! requires_foo(0i32); // works: through the impl we defined above +//! requires_foo(&0i32); // works: through the generated impl +//! requires_foo(Box::new(0i32)); // works: through the generated impl +//! ``` +//! +//! +//! # Basic syntax and supported types +//! +//! You can annotate your trait with the `#[auto_impl(...)]` attribute. That +//! attribute can only be used on traits and not on structs, enums or anything +//! else. +//! +//! In the attribute, you have to specify all so called *proxy types* (the +//! types you want to generate impls for) as a comma separated list. Each proxy +//! type has a short abbreviation that you have to list there. +//! +//! Currently the following proxy types are supported: +//! +//! | Abbreviation | Example generated impl | +//! | ------------ | ---------------------- | +//! | `&` | `impl Trait for &T` | +//! | `&mut` | `impl Trait for &mut T` | +//! | `Box` | `impl Trait for Box` | +//! | `Rc` | `impl Trait for Rc` | +//! | `Arc` | `impl Trait for Arc` | +//! | `Fn` | `impl Trait for T` | +//! | `FnMut` | `impl Trait for T` | +//! | `FnOnce` | `impl Trait for T` | +//! +//! +//! # More examples +//! +//! More examples can be found in [the examples folder][examples]. In +//! particular, the `greet_closure` example shows how to use the `Fn*` proxy +//! types. +//! +//! [examples]: https://github.com/auto-impl-rs/auto_impl/tree/master/examples +//! +//! The following example shows that a trait can contain associated consts, +//! associated types and complex methods (with generics, bounds, ...). +//! +//! ``` +//! use auto_impl::auto_impl; +//! use std::{fmt, rc::Rc}; +//! +//! +//! #[auto_impl(&, &mut, Box, Rc)] +//! trait Animal { +//! const NUMBER_OF_LEGS: u8; +//! +//! type Name: fmt::Display; +//! fn name(&self) -> Self::Name; +//! +//! fn select_favorite<'a, I>(&self, toys: I) -> &'a str +//! where +//! I: Iterator; +//! } +//! +//! struct Dog(String); +//! +//! impl Animal for Dog { +//! const NUMBER_OF_LEGS: u8 = 4; +//! +//! type Name = String; +//! fn name(&self) -> Self::Name { +//! self.0.clone() +//! } +//! +//! fn select_favorite<'a, I>(&self, mut toys: I) -> &'a str +//! where +//! I: Iterator +//! { +//! toys.next().unwrap() +//! } +//! } +//! +//! fn require_animal(_: impl Animal) {} +//! +//! // All these calls work, as the `#[auto_impl]` attribute generated four +//! // impls for all those proxy types +//! require_animal(Dog("Doggo".into())); +//! require_animal(&Dog("Doggo".into())); +//! require_animal(&mut Dog("Doggo".into())); +//! require_animal(Box::new(Dog("Doggo".into()))); +//! require_animal(Rc::new(Dog("Doggo".into()))); +//! ``` +//! +//! +//! # Restriction of references and smart pointers +//! +//! Not every trait can be implemented for every proxy type. As an easy +//! example, consider this trait: +//! +//! ``` +//! trait Bar { +//! fn bar(&mut self); +//! } +//! ``` +//! +//! If we try to implement it for immutable references via `#[auto_impl(&)]` +//! the following impl would be generated: +//! +//! ```ignore +//! impl Bar for &T { +//! fn bar(&mut self) { +//! T::bar(*self) // fails to compile +//! } +//! } +//! ``` +//! +//! As you can easily see, this won't work because we can't call `bar` through +//! an immutable reference. There are similar restrictions for many other +//! smartpointers and references. +//! +//! In the following table you can see which methods can be implemented for +//! which proxy type. If a trait contains at least one method that cannot be +//! implemented for a proxy type, you cannot implement the trait for that proxy +//! type. +//! +//! | Trait contains method with... | `&` | `&mut` | `Box` | `Rc` | `Arc` | +//! | ----------------------------- | --- | ------ | ----- | ---- | ----- | +//! | `&self` receiver | ✔ | ✔ | ✔ | ✔ | ✔ | +//! | `&mut self` receiver | ✗ | ✔ | ✔ | ✗ | ✗ | +//! | `self` receiver | ✗ | ✗ | ✔ | ✗ | ✗ | +//! | no `self` receiver | ✔ | ✔ | ✔ | ✔ | ✔ | +//! +//! References and smartpointers have **no restriction in regard to associated +//! types and associated consts**! Meaning: traits with associated types/consts +//! can always be implemented for references and smartpointers as long as the +//! methods of that trait can be implemented. +//! +//! +//! # Restriction of closure types (`Fn*` traits) +//! +//! The `Fn*` proxy types have a lot more restrictions than references and +//! smart pointer: +//! - the trait must not define any associated types or consts +//! - the trait must define **exactly one** method +//! - the method must have a `self` receiver +//! - the method must not return anything borrowed from `self` +//! - the method must not have generic type or const parameters +//! +//! Additionally, some `Fn*` traits cannot be implemented for all `self` +//! receiver types: +//! +//! | `self` Receiver | `Fn` | `FnMut` | `FnOnce` | +//! | --------------- | ---- | ------- | -------- | +//! | `&self` | ✔ | ✗ | ✗ | +//! | `&mut self` | ✔ | ✔ | ✗ | +//! | `self` | ✔ | ✔ | ✔ | +//! +//! Lastly, the impls generated for the `Fn*` proxy types contain `for T`. This +//! is the most general blanket impl. So just be aware of the problems with +//! coherence and orphan rules that can emerge due to this impl. +//! +//! +//! # The `keep_default_for` attribute for methods +//! +//! By default, the impls generated by `auto_impl` will overwrite all methods +//! of the trait, even those with default implementation. Sometimes, you want +//! to not overwrite default methods and instead use the default +//! implementation. You can do that by adding the +//! `#[auto_impl(keep_default_for(...))]` attribute to a default method. In the +//! parenthesis you need to list all proxy types for which the default method +//! should be kept. +//! +//! From [the `keep_default_for` example]( +//! https://github.com/auto-impl-rs/auto_impl/blob/master/examples/keep_default_for.rs): +//! +//! ``` +//! # use auto_impl::auto_impl; +//! #[auto_impl(&, Box)] +//! trait Foo { +//! fn required(&self) -> String; +//! +//! // The generated impl for `&T` will not override this method. +//! #[auto_impl(keep_default_for(&))] +//! fn provided(&self) { +//! println!("Hello {}", self.required()); +//! } +//! } +//! ``` +//! +//! +//! # The `nightly` feature gate +//! +//! By default, this crate compiles on stable (since 1.30.0). If you don't need +//! stable support, you can enable the `nightly` feature of this crate: +//! +//! ```toml +//! [dependencies] +//! auto_impl = { version = "*", features = ["nightly"] } +//! ``` +//! +//! The nightly feature enables a few additional features that are not +//! available on stable yet. Currently, you get these advantages: +//! - Better diagnostics (better spans and nicer to read on terminal) +//! - All idents generated by auto_impl use the `def_site` hygiene and +//! therefore will never ever have name collisions with user written idents. +//! Note that `auto_impl` already (even without nightly feature) takes care +//! that idents never collide, if possible. But `def_site` hygiene is still +//! technically the more correct solution. +//! #![cfg_attr( feature = "nightly", diff --git a/tests/compile-pass/big_trait_for_box.rs b/tests/compile-pass/big_trait_for_box.rs index 87570f4..4c3c679 100644 --- a/tests/compile-pass/big_trait_for_box.rs +++ b/tests/compile-pass/big_trait_for_box.rs @@ -12,4 +12,5 @@ trait BoxTrait1<'a, T: for<'b> Into<&'b str>> { fn execute2(&mut self, arg1: i32) -> Self::Type2; fn execute3(self) -> Self::Type1; fn execute4(arg1: String) -> Result; + fn execute5() -> String; } diff --git a/tests/compile-pass/generic_fn_method_for_refs.rs b/tests/compile-pass/generic_fn_method_for_refs.rs new file mode 100644 index 0000000..6d82561 --- /dev/null +++ b/tests/compile-pass/generic_fn_method_for_refs.rs @@ -0,0 +1,8 @@ +use auto_impl::auto_impl; + +#[auto_impl(&, Arc)] +pub trait OrderStoreFilter { + fn filter(&self, predicate: F) -> Result + where + F: Fn(&str) -> bool; +} diff --git a/tests/compile-pass/generic_types_and_lifetimes_for_fn.rs b/tests/compile-pass/generic_types_and_lifetimes_for_fn.rs new file mode 100644 index 0000000..f8d1ff9 --- /dev/null +++ b/tests/compile-pass/generic_types_and_lifetimes_for_fn.rs @@ -0,0 +1,6 @@ +use auto_impl::auto_impl; + +#[auto_impl(Fn)] +trait MyTrait<'a, T> { + fn execute<'b>(&'a self, arg1: &'b T, arg2: &'static str) -> Result<(), String>; +} diff --git a/tests/compile-pass/generic_types_and_lifetimes_for_refs.rs b/tests/compile-pass/generic_types_and_lifetimes_for_refs.rs new file mode 100644 index 0000000..b532c5e --- /dev/null +++ b/tests/compile-pass/generic_types_and_lifetimes_for_refs.rs @@ -0,0 +1,6 @@ +use auto_impl::auto_impl; + +#[auto_impl(&, &mut)] +trait MyTrait<'a, T> { + fn execute<'b>(&'a self, arg1: &'b T, arg2: &'static str) -> Result<(), String>; +}