From 595797cc98a429081e14178ee145b7420344420c Mon Sep 17 00:00:00 2001 From: Lyndon Brown Date: Fri, 19 Oct 2018 20:12:37 +0100 Subject: [PATCH] clarify discussion of macros and namspaces The existing text was rather unclear, for instance it might mislead a person into the false understanding that ALL macros within a crate are exported, regardless of export attribute (which is not mentioned). If the text is going to make references to namespace differences compared to functions and bringing them into scope and such, we might as well have something clear and truly informative. --- 2018-edition/src/appendix-04-macros.md | 74 +++++++++++++++++--------- 1 file changed, 49 insertions(+), 25 deletions(-) diff --git a/2018-edition/src/appendix-04-macros.md b/2018-edition/src/appendix-04-macros.md index c2ca46fbfa..d8b59b8ccc 100644 --- a/2018-edition/src/appendix-04-macros.md +++ b/2018-edition/src/appendix-04-macros.md @@ -44,27 +44,54 @@ generally more difficult to read, understand, and maintain than function definitions. Another difference between macros and functions is that macro definitions -aren’t namespaced within modules like function definitions are. To prevent -unexpected name clashes when using external crates, you have to explicitly -bring the macros into the scope of your project at the same time as you bring -the external crate into scope, using the `#[macro_use]` annotation. The -following example would bring all the macros defined in the `serde` crate into -the scope of the current crate: +aren’t namespaced within modules like function definitions are (and thus normal +`use` statements are not involved in bringing them into scope): + + - Within a crate: Macros defined within a module are, by default, private to + that module (and its submodules); They are automatically visible within all + submodules beneath the module that defines them; Tagging a module with a + `#[macro_use]` attribute brings the macros defined within (or within scope + of) that module into the visible scope of its parent. (Note, a macro does not + need to be defined at module level, it can privately be defined within a + function for example). + - Externally: Only macros tagged with a `#[macro_export]` attribute are visible + externally to users of a crate; All exported macros are made available at the + root of the crate's API only, and they must all have unique names; and + similar to how `#[macro_use]` is required to bring macros into scope of a + parent module, this is also required on an `extern crate` statement to use + exported macros (they are not imported by default to prevent unexpected name + clashes when using multiple external crates). + + The following example would bring all the macros defined in the `serde` crate + into the scope of the current crate: + + ```rust,ignore + #[macro_use] + extern crate serde; + ``` + +There is one last important difference between macros and functions: order of +definition (or import) and use is important! You must define or bring macros +into scope *before* you call them in a file, whereas you can define functions +anywhere and call them anywhere. Example: -```rust,ignore -#[macro_use] -extern crate serde; +```rust +// Note, attributing the `example` mod with `#[macro_use]` here would change +// the example, making `my_macros` macros available to everything within it. +mod example { + // At this point `my_macros` macros are not available. + mod foo; + + // From this point, they are available to everything defined below (within + // `example`). This also gives the parent `example` mod opportunity to + // bring them into its scope. + #[macro_use] + mod my_macros; + + mod bar; // Available within here for instance! +} ``` -If `extern crate` was able to bring macros into scope by default without this -explicit annotation, you would be prevented from using two crates that happened -to define macros with the same name. In practice, this conflict doesn’t occur -often, but the more crates you use, the more likely it is. - -There is one last important difference between macros and functions: you must -define or bring macros into scope *before* you call them in a file, whereas you -can define functions anywhere and call them anywhere. - ### Declarative Macros with `macro_rules!` for General Metaprogramming The most widely used form of macros in Rust are *declarative macros*. These are @@ -118,14 +145,11 @@ definition > includes code to preallocate the correct amount of memory up front. That code > is an optimization that we don’t include here to make the example simpler. -The `#[macro_export]` annotation indicates that this macro should be made -available whenever the crate in which we’re defining the macro is imported. -Without this annotation, even if someone depending on this crate uses the -`#[macro_use]` annotation, the macro wouldn’t be brought into scope. +The #[macro_export] annotation was mentioned above. -We then start the macro definition with `macro_rules!` and the name of the -macro we’re defining *without* the exclamation mark. The name, in this case -`vec`, is followed by curly brackets denoting the body of the macro definition. +We start the macro definition with `macro_rules!` and the name of the macro +we’re defining *without* the exclamation mark. The name, in this case `vec`, +is followed by curly brackets denoting the body of the macro definition. The structure in the `vec!` body is similar to the structure of a `match` expression. Here we have one arm with the pattern `( $( $x:expr ),* )`,