Single instruction, multiple data (SIMD) support is the most notable new feature in Rust 1.27, along with a more explicit syntax for traits.
SIMD support at the language level means developers can express vectorized computations at a higher-level and have a chance at outperforming the compiler when it is not smart enough to apply autovectorization. The following is an example of how you can express a sum of two vectors (or slices) of 16 elements each one byte long (u8
). Each slice fits one 128 bit register, so we can put the two slices into two registers and add them in a single CPU instruction using the new std::arch
module:
#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"),
target_feature = "avx2"))]
fn foo() {
#[cfg(target_arch = "x86")]
use std::arch::x86::_mm256_add_epi64;
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::_mm256_add_epi64;
unsafe {
_mm256_add_epi64(...);
}
}
The snippet above also shows how you can customize your SIMD instructions for specific platforms, since not all platforms your code should be able to run on may support SIMD operations. The first cfg
flag will output the following code only for the specified platforms (x86 or x86_64 provided they support AVX2
), while the inner cfg
flags will choose the right _mm256_add_epi64
instruction for the specific platform.
SIMD support provided by std::arch
is only the first step in Rust SIMD support. Indeed, a std::simd
module is already planned which should provide higher-level operations. An example of what could be possible in std::simd
is given through the faster
crate, which allows you to use simd_iter
instead of iter
, simd_map
instead of map
, etc., to use SIMD-ified versions of those basic vector operations.
Another new feature of the language aims to make the trait syntax more explicit and make it clearer when a given trait object corresponds to just one pointer or two of them. The basic syntax to represent a trait object is the following:
Box<Foo>
This syntax hides the fact that when Foo
is a structure it will be simply embedded inside Box
. On the contrary, it will be allocated on the heap if it is a trait and a pointer to its vtable
will be allocated on the stack. This is due to vtables in Rust not being stored with the data, as it happens in C++, but separately. To make things clearer, now Rust supports a new dyn Trait
syntax:
Box<dyn Foo>
&dyn Foo
&mut dyn Foo
The old syntax will remain in place and there are no defined plans for its deprecation.
As a final note, the #[must_use]
attribute can be now used on functions to make the compiler flag any situation where the return value of such functions is ignored:
#[must_use]
fn double(x: i32) -> i32 {
2 * x
}
fn main() {
double(4); // warning: unused return value of `double` which must be used
let _ = double(4); // (no warning)
}
To read more about Rust 1.27, do not miss the official release notes.