Series: The Rust Annals
Vol. I Issue 51 nlopes.dev
Announcing Rust 1.50.0
Stabilization of const-generic array indexing significantly boosts const generic utility, while union safety improvements and widely adopted standard library additions like f64::clamp and slice::fill mark it as a practically important release.
Const-generic array indexing
Continuing the march toward stable const generics, this release adds
implementations of ops::Index and IndexMut for arrays [T; N] for
any length of const N. The indexing operator [] already worked on
arrays through built-in compiler magic, but at the type level, arrays
didn’t actually implement the library traits until now.
fn second<C>(container: &C) -> &C::Output
where
C: std::ops::Index<usize> + ?Sized,
{
&container[1]
}
fn main() {
let array: [i32; 3] = [1, 2, 3];
assert_eq!(second(&array[..]), &2); // slices worked before
assert_eq!(second(&array), &2); // now it also works directly
}
const value repetition for arrays
Arrays in Rust can be written either as a list [a, b, c] or a repetition [x; N].
For lengths N greater than one, repetition has only been allowed for xs that are Copy,
and RFC 2203 sought to allow any const expression there. However,
while that feature was unstable for arbitrary expressions, its implementation
since Rust 1.38 accidentally allowed stable use of const values in array
repetition.
fn main() {
// This is not allowed, because `Option<Vec<i32>>` does not implement `Copy`.
let array: [Option<Vec<i32>>; 10] = [None; 10];
const NONE: Option<Vec<i32>> = None;
const EMPTY: Option<Vec<i32>> = Some(Vec::new());
// However, repeating a `const` value is allowed!
let nones = [NONE; 10];
let empties = [EMPTY; 10];
}
In Rust 1.50, that stabilization is formally acknowledged. In the future, to avoid such “temporary” named
constants, you can look forward to inline const expressions per RFC 2920.
Safe assignments to ManuallyDrop<T> union fields
Rust 1.49 made it possible to add ManuallyDrop<T> fields to a union as part
of allowing Drop for unions at all. However, unions don’t drop old values
when a field is assigned, since they don’t know which variant was formerly
valid, so safe Rust previously limited this to Copy types only, which never Drop.
Of course, ManuallyDrop<T> also doesn’t need to Drop, so now Rust 1.50
allows safe assignments to these fields as well.
A niche for File on Unix platforms
Some types in Rust have specific limitations on what is considered a
valid value, which may not cover the entire range of possible memory
values. We call any remaining invalid value a niche, and this space
may be used for type layout optimizations. For example, in Rust 1.28
we introduced NonZero integer types (like NonZeroU8) where 0 is a niche, and this allowed
Option<NonZero> to use 0 to represent None with no extra memory.
On Unix platforms, Rust’s File is simply made of the system’s integer
file descriptor, and this happens to have a possible niche
as well because it can never be -1! System calls which return a file
descriptor use -1 to indicate that an error occurred (check errno)
so it’s never possible for -1 to be a real file descriptor. Starting
in Rust 1.50 this niche is added to the type’s definition so it can be
used in layout optimizations too. It follows that Option<File> will
now have the same size as File itself!
Library changes
In Rust 1.50.0, there are nine new stable functions:
bool::thenbtree_map::Entry::or_insert_with_keyf32::clampf64::clamphash_map::Entry::or_insert_with_keyOrd::clampRefCell::takeslice::fillUnsafeCell::get_mut
And quite a few existing functions were made const:
IpAddr::is_ipv4IpAddr::is_ipv6Layout::sizeLayout::alignLayout::from_size_alignpowfor all integer types.checked_powfor all integer types.saturating_powfor all integer types.wrapping_powfor all integer types.next_power_of_twofor all unsigned integer types.checked_power_of_twofor all unsigned integer types.
See the detailed release notes to learn about other changes.