Appendix F - Newest Features

This appendix documents features that have been added to stable Rust since the main part of the book was completed.

Field init shorthand

We can initialize a data structure (struct, enum, union) with named fields, by writing fieldname as a shorthand for fieldname: fieldname. This allows a compact syntax for initialization, with less duplication:

#[derive(Debug)]
struct Person {
    name: String,
    age: u8,
}

fn main() {
    let name = String::from("Peter");
    let age = 27;

    // Using full syntax:
    let peter = Person { name: name, age: age };

    let name = String::from("Portia");
    let age = 27;

    // Using field init shorthand:
    let portia = Person { name, age };

    println!("{:?}", portia);
}

Returning from loops

One of the uses of a loop is to retry an operation you know can fail, such as checking if a thread completed its job. However, you might need to pass the result of that operation to the rest of your code. If you add it to the break expression you use to stop the loop, it will be returned by the broken loop:

fn main() {
    let mut counter = 0;

    let result = loop {
        counter += 1;

        if counter == 10 {
            break counter * 2;
        }
    };

    assert_eq!(result, 20);
}

Nested groups in use declarations

If you have a complex module tree with many different submodules and you need to import a few items from each one, it might be useful to group all the imports in the same declaration to keep your code clean and avoid repeating the base modules' name.

The use declaration supports nesting to help you in those cases, both with simple imports and glob ones. For example this snippets imports bar, Foo, all the items in baz and Bar:

# #![allow(unused_imports, dead_code)]
#
# mod foo {
#     pub mod bar {
#         pub type Foo = ();
#     }
#     pub mod baz {
#         pub mod quux {
#             pub type Bar = ();
#         }
#     }
# }
#
use foo::{
    bar::{self, Foo},
    baz::{*, quux::Bar},
};
#
# fn main() {}

Inclusive ranges

Previously, when a range (.. or ...) was used as an expression, it had to be .., which is exclusive of the upper bound, while patterns had to use ..., which is inclusive of the upper bound. Now, ..= is accepted as syntax for inclusive ranges in both expression and range context:

fn main() {
    for i in 0 ..= 10 {
        match i {
            0 ..= 5 => println!("{}: low", i),
            6 ..= 10 => println!("{}: high", i),
            _ => println!("{}: out of range", i),
        }
    }
}

The ... syntax is still accepted in matches, but it is not accepted in expressions. ..= should be preferred.

128-bit integers

Rust 1.26.0 added 128-bit integer primitives:

  • u128: A 128-bit unsigned integer with range [0, 2^128 - 1]
  • i128: A 128-bit signed integer with range [-(2^127), 2^127 - 1]

These primitives are implemented efficiently via LLVM support. They are available even on platforms that don't natively support 128-bit integers and can be used like the other integer types.

These primitives can be very useful for algorithms that need to use very large integers efficiently, such as certain cryptographic algorithms.