custom attributes and error reporting docs for procedural macros
This commit is contained in:
parent
ea8c62919e
commit
5189c4f8c1
1 changed files with 69 additions and 1 deletions
|
|
@ -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!
|
||||
```
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue