@@ -209,5 +209,73 @@ Ok so now, let's compile `hello-world`. Executing `cargo run` now yields:
209
209
Hello, World! My name is FrenchToast
210
210
Hello, World! My name is Waffles
211
211
```
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.
212
215
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