Skip to content

Commit 5189c4f

Browse files
committed
custom attributes and error reporting docs for procedural macros
1 parent ea8c629 commit 5189c4f

File tree

1 file changed

+69
-1
lines changed

1 file changed

+69
-1
lines changed

src/doc/book/src/procedural-macros.md

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,5 +209,73 @@ Ok so now, let's compile `hello-world`. Executing `cargo run` now yields:
209209
Hello, World! My name is FrenchToast
210210
Hello, World! My name is Waffles
211211
```
212+
## Custom Attributes
213+
In some cases it might make sense to allow users some kind of configuration.
214+
For our example the user might want to overwrite the name that is printed in the `hello_world()` method.
212215

213-
We've done it!
216+
This can be achieved with custom attributes:
217+
```rust,ignore
218+
#[derive(HelloWorld)]
219+
#[HelloWorldName = "the best Pancakes"]
220+
struct Pancakes;
221+
222+
fn main() {
223+
Pancakes::hello_world();
224+
}
225+
```
226+
227+
If we try to compile this though, the compiler will respond with an error:
228+
229+
```
230+
error: The attribute `HelloWorldName` is currently unknown to the compiler and may have meaning added to it in the future (see issue #29642)
231+
```
232+
233+
The compiler needs to know that we handle this attribute and to not respond with an error.
234+
This is done in the `hello-world-derive`-crate by adding `attributes` to the `proc_macro_derive` attribute:
235+
236+
```rust,ignore
237+
#[proc_macro_derive(HelloWorld, attributes(HelloWorldName))]
238+
pub fn hello_world(input: TokenStream) -> TokenStream
239+
```
240+
241+
Multiple attributes can be specified that way.
242+
243+
244+
## Raising Errors
245+
Let's assume that we do not want to accept `Enums` as input to our custom derive method.
246+
247+
This condition can be easily checked with the help of `syn`.
248+
But how to we tell the user, that we do not accept `Enums`.
249+
The idiomatic was to report errors in procedural macros is to panic:
250+
251+
```rust,ignore
252+
fn impl_hello_world(ast: &syn::MacroInput) -> quote::Tokens {
253+
let name = &ast.ident;
254+
// Check if derive(HelloWorld) was specified for a struct
255+
if let syn::Body::Struct(_) = ast.body {
256+
// Yes, this is a struct
257+
quote! {
258+
impl HelloWorld for #name {
259+
fn hello_world() {
260+
println!("Hello, World! My name is {}", stringify!(#name));
261+
}
262+
}
263+
}
264+
} else {
265+
//Nope. This is an Enum. We cannot handle these!
266+
panic!("#[derive(HelloWorld)] is only defined for structs, not for enums!");
267+
}
268+
}
269+
```
270+
271+
If a user now tries to derive `HelloWorld` from an enum they will be greeted with following, hopefully helpful, error:
272+
273+
```
274+
error: custom derive attribute panicked
275+
--> src/main.rs
276+
|
277+
| #[derive(HelloWorld)]
278+
| ^^^^^^^^^^
279+
|
280+
= help: message: #[derive(HelloWorld)] is only defined for structs, not for enums!
281+
```

0 commit comments

Comments
 (0)