The Rust development team is pleased to announce the release of a new version of Rust: 1.30.0. Rust is a system programming language aimed at security, speed, and parallel code execution.
If you have a previous version of Rust installed using rustup
, then to update Rust to version 1.30.0, you just need to run:
$ rustup update stable
If you have not yet installed rustup
, you can install it from the corresponding page of our website. Detailed notes for the release of Rust 1.30.0 can be found on GitHub.
Rust 1.30 is an outstanding release with a number of important innovations. But on Monday, the official blog will be published, please check the beta version of Rust 1.31, which will be the first release of "Rust 2018". For more information about this, see our previous publication "What is Rust 2018" .
Back in Rust 1.15, we added the ability to define "custom derive macros". For example, using serde_derive
, you can declare:
#[derive(Serialize, Deserialize, Debug)] struct Pet { name: String, }
And convert Pet
to JSON and back to structure using serde_json
. This is possible thanks to the automatic Deserialize
Serialize
and Deserialize
types using procedural macros in serde_derive
.
Rust 1.30 expands the functionality of procedural macros by adding the ability to define two other types of macros: "attribute procedural macros" and "functional procedural macros".
Attribute macros are similar to derive macros for automatic output, but instead of generating code only for the #[derive]
attribute, they allow users to create their own new attributes. This makes them more flexible: derive macros only work for structures and enums, but attributes can be applied to other objects, such as functions. For example, attribute macros allow you to do the following when using a web framework:
#[route(GET, "/")] fn index() {
This #[route]
attribute will be defined in the framework itself as a procedural macro. Its signature will look like this:
#[proc_macro_attribute] pub fn route(attr: TokenStream, item: TokenStream) -> TokenStream {
Here we have two input parameters of the TokenStream
type: the first is for the content of the attribute itself, that is, these are the GET, "/"
parameters. The second is the body of the object to which the attribute is applied. In our case, this is fn index() {}
and the rest of the function body.
Functional macros define such macros, the use of which looks like a function call. For example, the macro sql!
:
let sql = sql!(SELECT * FROM posts WHERE id=1);
Inside this macro, it will parse SQL expressions and check them for syntactic correctness. A similar macro should be declared as follows:
#[proc_macro] pub fn sql(input: TokenStream) -> TokenStream {
This is similar to the derive macro signature: we get the tokens that are inside the parentheses, and return the code generated from them.
use
You can now import macros into scope using the use keyword . For example, to use the json
macro from the serde-json
package, the entry used was:
#[macro_use] extern crate serde_json; let john = json!({ "name": "John Doe", "age": 43, "phones": [ "+44 1234567", "+44 2345678" ] });
And now you will have to write:
extern crate serde_json; use serde_json::json; let john = json!({ "name": "John Doe", "age": 43, "phones": [ "+44 1234567", "+44 2345678" ] });
Here, the macro is imported as well as other elements, so there is no need to use the macro_use
annotation.
Finally, the proc_macro package is stabilized , which provides the API needed for writing procedural macros. It also significantly improved the error handling API, and packages like syn
and quote
already using it. For example, before:
#[derive(Serialize)] struct Demo { ok: String, bad: std::thread::Thread, }
led to this error:
error[E0277]: the trait bound `std::thread::Thread: _IMPL_SERIALIZE_FOR_Demo::_serde::Serialize` is not satisfied --> src/main.rs:3:10 | 3 | #[derive(Serialize)] | ^^^^^^^^^ the trait `_IMPL_SERIALIZE_FOR_Demo::_serde::Serialize` is not implemented for `std::thread::Thread`
Now it will be issued:
error[E0277]: the trait bound `std::thread::Thread: serde::Serialize` is not satisfied --> src/main.rs:7:5 | 7 | bad: std::thread::Thread, | ^^^ the trait `serde::Serialize` is not implemented for `std::thread::Thread`
The module system has long become a sore spot for newcomers to Rust; some of its rules were inconvenient in practice. These changes are the first step we take to simplify the system of modules.
In addition to the above change for macros, there are two new improvements in the use of use
. First, external packages are now added to the prelude , that is:
// let json = ::serde_json::from_str("..."); // let json = serde_json::from_str("...");
The catch is that the old style was not always needed because of the peculiarities of the Rust modules system:
extern crate serde_json; fn main() { // ; , `serde_json` // let json = serde_json::from_str("..."); } mod foo { fn bar() { // ; `foo`, `serde_json` // let json = serde_json::from_str("..."); } // - `use` use serde_json; fn baz() { // - `::serde_json`, // , let json = ::serde_json::from_str("..."); } }
It was unpleasant to get broken code just by moving the function to a submodule. Now the first part of the path will be checked, and if it corresponds to some extern crate
, then it will be used regardless of the position of the call in the hierarchy of modules.
Finally, use began to support the import of elements into the current scope with paths that begin with crate :
mod foo { pub fn bar() { // ... } } // use ::foo::bar; // use foo::bar; // use crate::foo::bar;
The crate
keyword at the beginning of the path indicates that the path will start from the root of the package. Previously, the paths specified in the use
import line were always indicated relative to the package root, but the paths in the rest of the code that directly refer to elements were indicated relative to the current module, which led to contradictory behavior of the paths:
mod foo { pub fn bar() { // ... } } mod baz { pub fn qux() { // ::foo::bar(); // , `use`: // foo::bar(); // crate::foo::bar(); } }
As soon as the new style becomes widely used, it will hopefully make the absolute paths more clear without needing to use the ugly prefix ::
.
All these changes combine to make it easier to understand how paths are resolved. In any place where you see the path a::b::c
, besides the use
statement, you can ask:
a
package name? Then you need to look for b::c
inside it.a
crate
keyword? Then you need to look for b::c
from the root of the current package.a::b::c
from the current position in the hierarchy of modules.The old behavior of paths in use
, always starting from the root of the package, is still applicable. But in the case of a one-time transition to a new style, these rules will be applied to paths everywhere uniformly, and you will have to take much less care of imports when moving the code.
You can now use keywords as identifiers using the following new syntax:
// `for` let r#for = true; // `for` fn r#for() { // ... } // r#for();
There are not many cases when it will be useful to you. But once you try to use the package for Rust 2015 in the project for Rust 2018 or vice versa, then the set of keywords will be different. We will tell about it in more detail in the upcoming announcement of Rust 2018.
Back in Rust 1.6, we announced stabilization of "no_std" and libcore for creating projects without a standard library. However, with one clarification: it was possible to create only libraries, but not applications.
In Rust 1.30, you can use the attribute #[panic_handler]
for self-realization of panic. This means that you can now create applications, not just libraries that do not use the standard library.
One last thing: in macros, you can now map scope modifiers , such as pub
, using the vis
specifier. Additionally, "instrumental attributes", such as #[rustfmt::skip]
, are now stabilized . True for use with static analysis tools like #[allow(clippy::something)]
, they are not yet stable.
See the release notes for details.
The following APIs have been stabilized in this release:
Ipv4Addr::{BROADCAST, LOCALHOST, UNSPECIFIED}
Ipv6Addr::{BROADCAST, LOCALHOST, UNSPECIFIED}
Iterator::find_map
In addition, the standard library has long had functions for removing spaces on one side of some text, such as trim_left
. However, for RTL languages, the meaning of "right" and "left" here leads to confusion. Therefore, we introduce new names for these functions:
trim_left
-> trim_start
trim_right
-> trim_end
trim_left_matches
-> trim_start_matches
trim_right_matches
-> trim_end_matches
We plan to declare obsolete the old names (but not delete, of course) in Rust 1.33.
See the release notes for details.
Cargo's biggest improvement in this release is that we now have a progress bar!
See the release notes for details.
Many people co-created Rust 1.30. We could not complete the work without the participation of each of you. Thank!
From the translator: I express special thanks to the members of the Rustycrate community and personally vitvakatu and Virtuos86 for their help with translation and proofreading.
Source: https://habr.com/ru/post/428073/
All Articles