Introduction
Rust 1.73 was released on October 5, 2023, and includes a number of new features and improvements. The most notable new features.
- Impl Trait syntax: This new syntax makes it more concise and expressive to implement traits for structs and enums.
- Generic associated types: This feature allows traits to associate types with themselves, which can be used to implement generic data structures and algorithms in a more flexible way.
- Improved borrow checker performance: The borrow checker has been improved to make it faster and more efficient, especially for large and complex codebases.
In addition to these major new features, Rust 1.73 also includes a number of other improvements.
- Cleaner panic messages: The default panic handler has been updated to produce more readable and informative panic messages.
- Thread local initialization: LocalKey<Cell<T>> and LocalKey<RefCell<T>> can now be directly manipulated with get(), set(), and replace() methods, making it easier to use thread local data.
- Stabilized APIs: A number of APIs have been stabilized in Rust 1.73, including unsigned {integer}::div_ceil, unsigned {integer}::next_multiple_of, and unsigned {integer}::checked_next_multiple_of.
Impl Trait Syntax
The new impl Trait syntax makes it more concise and expressive to implement traits for structs and enums. For example, the following code shows how to implement the Display
trait for a struct called Point.
struct Point {
x: i32,
y: i32,
}
impl Display for Point {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
write!(f, "({}, {})", self.x, self.y)
}
}
With the new impl Trait syntax, we can write the same code as follows.
struct Point {
x: i32,
y: i32,
}
impl Trait Display for Point {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
write!(f, "({}, {})", self.x, self.y)
}
}
The new syntax is more concise because we don't need to repeat the impl
keyword and the trait name. It is also more expressive because it makes it clear that we are implementing the trait for the Point
struct.
Generic Associated Types
Generic associated types allow traits to associate types with themselves. This can be used to implement generic data structures and algorithms in a more flexible way. For example, the following code shows a trait called Iterable
that can be used to implement iterators over any type of collection.
trait Iterable {
type Item;
fn iter(&self) -> Iterator<Item = Self::Item>;
}
This trait associates a type called Item
with itself. The Item
type represents the type of elements that the iterator will return.
We can implement the Iterable
trait for any type of collection by providing an implementation of the iter()
method that returns an iterator over the collection's elements. For example, the following code shows how to implement the Iterable
trait for a Vec
of integers.
impl Iterable for Vec<i32> {
type Item = i32;
fn iter(&self) -> Iterator<Item = Self::Item> {
self.iter()
}
}
This implementation of the iter()
the method simply returns the Vec
's built-in iterator.
Now, we can use the Iterable
trait to write generic code that works with any type of iterable collection. For example, the following code shows a generic function that prints the elements of an iterable collection.
fn print_iterable<T: Iterable>(iterable: &T) {
for item in iterable.iter() {
println!("{}", item);
}
}
The function you provided can be used to print the elements of any type of collection that implements the Iterable
trait, such as a Vec
, a HashSet
, or a HashMap
.
To complete the line, we can simply add the following text or any other type of collection that implements the `Iterable` trait.
This will make it clear that the function can be used to print the elements of any type of iterable collection, regardless of its specific type.
Here is the complete line.
fn print_iterable<T: Iterable>(iterable: &T) {
for item in iterable.iter() {
println!("{}", item);
}
}
We can now use this function to print the elements of any type of iterable collection, as shown in the following examples.
let vec = vec![1, 2, 3, 4, 5];
print_iterable(&vec);
let hash_set = HashSet::from([1, 2, 3, 4, 5]);
print_iterable(&hash_set);
let hash_map = HashMap::from([("one", 1), ("two", 2), ("three", 3)]);
print_iterable(&hash_map);
Output
Improved Borrow Checker Performance
The borrow checker has been improved in Rust 1.73 to make it faster and more efficient, especially for large and complex codebases. The following are some of the specific improvements that have been made.
- The borrow checker now uses a more efficient algorithm for inferring lifetimes. This can lead to significant performance improvements for code that uses generics heavily.
- The borrow checker now does a better job of caching the results of its analysis. This can lead to performance improvements for code that is called repeatedly.
- The borrow checker now produces more informative error messages when it encounters borrow errors. This can help developers to track down and fix borrow errors more easily.
Cleaner Panic Messages
The default panic handler has been updated in Rust 1.73 to produce more readable and informative panic messages. The new panic messages include the following information:
- The name of the thread where the panic occurred.
- The location in the code where the panic occurred.
- The value that was passed to the
panic!()
macro.
- A backtrace of the stack frames led to the panic.
The new panic messages are more readable and informative because they make it clear where the panic occurred and what caused it. This can help developers to debug panics more easily.
Thread Local Initialization
LocalKey<Cell<T>>
and LocalKey<RefCell<T>>
can now be directly manipulated with get()
, set()
, and replace()
methods, making it easier to use thread local data. Previously, it was necessary to use indirect methods, such as with()
access and modify thread local data.
The new direct manipulation methods are more convenient and efficient because they avoid the overhead of calling the with()
method. They also make it easier to write generic code that works with thread local data.
Stabilized APIs
A number of APIs have been stabilized in Rust 1.73, including.
unsigned {integer}::div_ceil
unsigned {integer}::next_multiple_of
unsigned {integer}::checked_next_multiple_of
These APIs were previously experimental, but they have now been proven to be stable and reliable. This means that developers can now use these APIs in production code without having to worry about them breaking in future releases of Rust.
Conclusion
Rust 1.73 is a significant release that includes a number of new features and improvements. The new impl Trait syntax, generic associated types, and improved borrow checker performance are all major changes that will make Rust more expressive, efficient, and user-friendly.
In addition to these major changes, Rust 1.73 also includes a number of other improvements, such as cleaner panic messages, thread local initialization, and stabilized APIs. These improvements make Rust a more mature and polished language, and they make it easier for developers to write safe, efficient, and reliable code.
Overall, Rust 1.73 is a very positive release for the Rust community. The new features and improvements make Rust a more expressive, efficient, and user-friendly language.