The latest release of Rust, version 1.30 extends procedural macros by allowing them to define new attributes and function-like macros. Additionally, it streamlines the Rust module system by making it more consistent and straightforward.
Rust 1.30 introduces two new types of procedural macros, "attribute-like procedural macros" and "function-like procedural macros". Procedural macros are Rust metaprogramming foundation and enable the manipulation of a program syntax tree. In this respect procedural macros are much more powerful than declarative macros, which provide a mechanism to define a shorthand for more complex code based on pattern matching.
Attribute-like procedural macros are similar to existing derive macros but are more flexible in that they allow you to create new attributes and may be applied also to functions in addition to structs and enums. For example, an attribute macro could enable the specification of a route
attribute to define HTTP routing:
// use of route procedural macro
#[route(GET, "/")]
fn index() {
...
}
// procedural macro defining route
#[proc_macro_attribute]
pub fn route(attr: TokenStream, item: TokenStream) -> TokenStream {
// attr receives the GET, "/" part of the macro
// item receives fn index () { ...
}
Similarly, function-like procedural macros allows you to define macros that look like functions, e.g.:
// parse an SQL statement
let sql = sql!(SELECT * FROM posts WHERE id=1);
#[proc_macro]
pub fn sql(input: TokenStream) -> TokenStream {
In both examples, TokenStream
represents the syntax tree the attribute is applied to or the attribute/function definition. The route
/sql
function converts the received syntax tree into a new syntax tree which is returned to the caller, i.e., generating new code to execute.
Rust 1.30 also brings a few changes to the use
macro to improve developer experience when using Rust module system. Firstly, use
can now bring in a macro definition, thus making the macro_use
annotation obsolete:
// old:
#[macro_use]
extern crate serde_json;
// new:
extern crate serde_json;
use serde_json::json;
Additionally, external crates are now more resilient to functions being moved across the module hierarchy by ensuring all references to a namespace are checked against all extern crate
directives included in the module prelude and using the one that matches, if any. Previously, you had to explicitly use extern
inside of a module or use the ::extern_name
syntax, as shown in the following example:
extern crate serde_json;
fn main() {
let json = serde_json::from_str("..."); // OK
}
mod foo {
// to use serde_json in this module you explicitly use it
use serde_json;
fn bar() {
let json = serde_json::from_str("...");
}
fn baz() {
// alternatively, you fully qualify the external module name
let json = ::serde_json::from_str("...");
}
Finally, use
is now more consistent in the way it interprets module paths. You can use now the crate
keyword to indicate that you would like the module path to start at your crate root. Previous to 1.30, this was the default for module paths but paths referring to items directly would start at the local path:
mod foo {
pub fn bar() {
// ...
}
}
mod baz {
pub fn qux() {
// old
::foo::bar();
// does not work, which is different than with `use`:
// foo::bar();
// new
crate::foo::bar();
}
}
More changes brought by Rust 1.30 are the following:
- You can now use keywords as identifiers by prefixing them with
r#
, e.g.r#for
. This change is mostly motivated by the fact that Rust 2018 will introduce new keywords, so a mechanism shall be available to convert existing code using those keywords as variable or function names. - It is now possible to build applications not using the standard library with
no_std
. Previously, you could only build libraries withno_std
due to the impossibility of defining a panic handler.
You can update your Rust distribution using $ rustup update stable
. For full detail of Rust 1.30 do not miss the release notes.