diff options
| -rw-r--r-- | src/doc/book/src/procedural-macros.md | 70 |
1 files changed, 69 insertions, 1 deletions
diff --git a/src/doc/book/src/procedural-macros.md b/src/doc/book/src/procedural-macros.md index d286c3b7bdc..ef8b638dc1c 100644 --- a/src/doc/book/src/procedural-macros.md +++ b/src/doc/book/src/procedural-macros.md @@ -209,5 +209,73 @@ Ok so now, let's compile `hello-world`. Executing `cargo run` now yields: Hello, World! My name is FrenchToast Hello, World! My name is Waffles ``` +## Custom Attributes +In some cases it might make sense to allow users some kind of configuration. +For our example the user might want to overwrite the name that is printed in the `hello_world()` method. -We've done it! +This can be achieved with custom attributes: +```rust,ignore +#[derive(HelloWorld)] +#[HelloWorldName = "the best Pancakes"] +struct Pancakes; + +fn main() { + Pancakes::hello_world(); +} +``` + +If we try to compile this though, the compiler will respond with an error: + +``` +error: The attribute `HelloWorldName` is currently unknown to the compiler and may have meaning added to it in the future (see issue #29642) +``` + +The compiler needs to know that we handle this attribute and to not respond with an error. +This is done in the `hello-world-derive`-crate by adding `attributes` to the `proc_macro_derive` attribute: + +```rust,ignore +#[proc_macro_derive(HelloWorld, attributes(HelloWorldName))] +pub fn hello_world(input: TokenStream) -> TokenStream +``` + +Multiple attributes can be specified that way. + + +## Raising Errors +Let's assume that we do not want to accept `Enums` as input to our custom derive method. + +This condition can be easily checked with the help of `syn`. +But how to we tell the user, that we do not accept `Enums`. +The idiomatic was to report errors in procedural macros is to panic: + +```rust,ignore +fn impl_hello_world(ast: &syn::MacroInput) -> quote::Tokens { + let name = &ast.ident; + // Check if derive(HelloWorld) was specified for a struct + if let syn::Body::Struct(_) = ast.body { + // Yes, this is a struct + quote! { + impl HelloWorld for #name { + fn hello_world() { + println!("Hello, World! My name is {}", stringify!(#name)); + } + } + } + } else { + //Nope. This is an Enum. We cannot handle these! + panic!("#[derive(HelloWorld)] is only defined for structs, not for enums!"); + } +} +``` + +If a user now tries to derive `HelloWorld` from an enum they will be greeted with following, hopefully helpful, error: + +``` +error: custom derive attribute panicked + --> src/main.rs + | + | #[derive(HelloWorld)] + | ^^^^^^^^^^ + | + = help: message: #[derive(HelloWorld)] is only defined for structs, not for enums! +``` |
