From 489b484ce39f5870fca6a104ca88b140be9f01cf Mon Sep 17 00:00:00 2001 From: Lukas Kalbertodt Date: Sat, 6 Oct 2018 15:10:33 +0200 Subject: [PATCH 1/5] Add crate documentation --- src/lib.rs | 224 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 223 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index f1dede1..d4f8deb 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 have to annotate your trait with the `#[auto_impl(...)]` attribute. You +//! can only use that attribute 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("Niko".into())); +//! require_animal(&Dog("Alex".into())); +//! require_animal(&mut Dog("Steve".into())); +//! require_animal(Box::new(Dog("Carol".into()))); +//! require_animal(Rc::new(Dog("Aaron".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", From fe31b0cfa1cd97cf06712c549f135bbd5bc2e8a7 Mon Sep 17 00:00:00 2001 From: Lukas Kalbertodt Date: Sat, 6 Oct 2018 15:20:06 +0200 Subject: [PATCH 2/5] Add new tests from old README examples Make sure that we won't delete any useful examples when I will rewrite the README in the next commit --- tests/compile-pass/big_trait_for_box.rs | 1 + tests/compile-pass/generic_fn_method_for_refs.rs | 8 ++++++++ tests/compile-pass/generic_types_and_lifetimes_for_fn.rs | 6 ++++++ .../compile-pass/generic_types_and_lifetimes_for_refs.rs | 6 ++++++ 4 files changed, 21 insertions(+) create mode 100644 tests/compile-pass/generic_fn_method_for_refs.rs create mode 100644 tests/compile-pass/generic_types_and_lifetimes_for_fn.rs create mode 100644 tests/compile-pass/generic_types_and_lifetimes_for_refs.rs 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>; +} From be401596a4b6f4e54d96305a79ca6eb1505185ca Mon Sep 17 00:00:00 2001 From: Lukas Kalbertodt Date: Sat, 6 Oct 2018 15:32:24 +0200 Subject: [PATCH 3/5] Update README - Add docs.rs badge - Remove all old examples and explanation (this is mostly in the crate docs now) - Add a new simple example (the same as in the crate docs) - Add license section --- README.md | 214 ++++++------------------------------------------------ 1 file changed, 24 insertions(+), 190 deletions(-) diff --git a/README.md b/README.md index ca7eb5d..e68566c 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. From 76b3443740511b17da4decfdc0e9e03171861137 Mon Sep 17 00:00:00 2001 From: Lukas Kalbertodt Date: Sat, 6 Oct 2018 15:37:02 +0200 Subject: [PATCH 4/5] Add `examples/README.md` to explain all examples --- examples/README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 examples/README.md 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 +``` From 7d44b6c6935350bf8d6f382c9d61d9383c29d130 Mon Sep 17 00:00:00 2001 From: Lukas Kalbertodt Date: Mon, 15 Oct 2018 10:24:16 +0200 Subject: [PATCH 5/5] Address comments on PR --- README.md | 4 ++-- src/lib.rs | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index e68566c..475ea3a 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,8 @@ use auto_impl::auto_impl; Add an `auto_impl` attribute to traits you want to automatically implement for wrapper types. Here is a small example: ```rust -// This will generate two additional impl blocks: one `for &T` and one -// `for Box` where `T: Foo`. +// 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); diff --git a/src/lib.rs b/src/lib.rs index d4f8deb..3c2fcb2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,8 +28,8 @@ //! //! # Basic syntax and supported types //! -//! You have to annotate your trait with the `#[auto_impl(...)]` attribute. You -//! can only use that attribute on traits and not on structs, enums or anything +//! 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 @@ -100,11 +100,11 @@ //! //! // All these calls work, as the `#[auto_impl]` attribute generated four //! // impls for all those proxy types -//! require_animal(Dog("Niko".into())); -//! require_animal(&Dog("Alex".into())); -//! require_animal(&mut Dog("Steve".into())); -//! require_animal(Box::new(Dog("Carol".into()))); -//! require_animal(Rc::new(Dog("Aaron".into()))); +//! 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()))); //! ``` //! //!