Rust Blog: Posts

Rust Blog

Announcing Rust 1.66.0

The Rust team is happy to announce a new version of Rust, 1.66.0. Rust is a programming language empowering everyone to build reliable and efficient software.

If you have a previous version of Rust installed via rustup, you can get 1.66.0 with:

rustup update stable

If you don't have it already, you can getrustup from the appropriate page on our website, and check out the detailed release notes for 1.66.0on GitHub.

If you'd like to help us out by testing future releases, you might consider updating locally to use the beta channel (rustup default beta) or the nightly channel (rustup default nightly). Pleasereport any bugs you might come across!

What's in 1.66.0 stable

Explicit discriminants on enums with fields

Enums with integer representations can now use explicit discriminants, even when they have fields.

#[repr(u8)]
enum Foo {
    A(u8),
    B(i8),
    C(bool) = 42,
}

Previously, you could use explicit discriminants on enums with representations, but only if none of their variants had fields. Explicit discriminants are useful when passing values across language boundaries where the representation of the enum needs to match in both languages. For example,

#[repr(u8)]
enum Bar {
    A,
    B,
    C = 42,
}

Here the Bar enum is guaranteed to have the same layout as u8. Each variant will use either the specified discriminant value or default to starting with 0.

assert_eq!(0, Bar::A as u8);
assert_eq!(1, Bar::B as u8);
assert_eq!(42, Bar::C as u8);

You could even add fields to enums with #[repr(Int)], and they would be laid out in a predictable way. Previously, however, you could not use these features together. That meant that making Foo::C's discriminant equal to 42 as above would be harder to achieve. You would need to add 41 hidden variants in between as a workaround with implicit discriminants!

Starting in Rust 1.66.0, the above example compiles, allowing you to use explicit discriminants on any enum with a #[repr(Int)] attribute.

core::hint::black_box

When benchmarking or examining the machine code produced by a compiler, it's often useful to prevent optimizations from occurring in certain places. In the following example, the function push_cap executes Vec::push 4 times in a loop:

fn push_cap(v: &mut Vec<i32>) {
    for i in 0..4 {
        v.push(i);
    }
}

pub fn bench_push() -> Duration { 
    let mut v = Vec::with_capacity(4);
    let now = Instant::now();
    push_cap(&mut v);
    now.elapsed()
}

If you inspect the optimized output of the compiler on x86_64, you'll notice that it looks rather short:

example::bench_push:
  sub rsp, 24
  call qword ptr [rip + std::time::Instant::now@GOTPCREL]
  lea rdi, [rsp + 8]
  mov qword ptr [rsp + 8], rax
  mov dword ptr [rsp + 16], edx
  call qword ptr [rip + std::time::Instant::elapsed@GOTPCREL]
  add rsp, 24
  ret

In fact, the entire function push_cap we wanted to benchmark has been optimized away!

We can work around this using the newly stabilized black_box function. Functionally, black_box is not very interesting: it takes the value you pass it and passes it right back. Internally, however, the compiler treats black_box as a function that could do anything with its input and return any value (as its name implies).

This is very useful for disabling optimizations like the one we see above. For example, we can hint to the compiler that the vector will actually be used for something after every iteration of the for loop.

use std::hint::black_box;

fn push_cap(v: &mut Vec<i32>) {
    for i in 0..4 {
        v.push(i);
        black_box(v.as_ptr());
    }
}

Now we can find the unrolled for loop in our optimized assembly output:

  mov dword ptr [rbx], 0
  mov qword ptr [rsp + 8], rbx
  mov dword ptr [rbx + 4], 1
  mov qword ptr [rsp + 8], rbx
  mov dword ptr [rbx + 8], 2
  mov qword ptr [rsp + 8], rbx
  mov dword ptr [rbx + 12], 3
  mov qword ptr [rsp + 8], rbx

You can also see a side effect of calling black_box in this assembly output. The instruction mov qword ptr [rsp + 8], rbx is uselessly repeated after every iteration. This instruction writes the address v.as_ptr() as the first argument of the function, which is never actually called.

Notice that the generated code is not at all concerned with the possibility of allocations introduced by the push call. This is because the compiler is still using the fact that we called Vec::with_capacity(4) in the bench_push function. You can play around with the placement of black_box, or try using it in multiple places, to see its effects on compiler optimizations.

cargo remove

In Rust 1.62.0 we introduced cargo add, a command line utility to add dependencies to your project. Now you can use cargo remove to remove dependencies.

Stabilized APIs

Other changes

There are other changes in the Rust 1.66 release, including:

  • You can now use ..=X ranges in patterns.
  • Linux builds now optimize the rustc frontend and LLVM backend with LTO and BOLT, respectively, improving both runtime performance and memory usage.

Check out everything that changed inRust,Cargo, and Clippy.

Contributors to 1.66.0

Many people came together to create Rust 1.66.0. We couldn't have done it without all of you.Thanks!

Continue Reading…

Rust Blog

Launching the 2022 State of Rust Survey

The 2022 State of Rust Survey is here!

It's that time again! Time for us to take a look at who the Rust community is composed of, how the Rust project is doing, and how we can improve the Rust programming experience. The Rust Survey working group is pleased to announce our 2022 State of Rust Survey! Whether or not you use Rust today, we want to know your opinions. Your responses will help the project understand its strengths and weaknesses, and establish development priorities for the future.

Completing this survey should take about 5–20 minutes and is anonymous. We will be accepting submissions for the next two weeks (until the 19th of December), and we will share our findings on blog.rust-lang.org sometime in early 2023. You can also check out last year’s results.

We're happy to be offering the survey in the following languages. If you speak multiple languages, please pick one.

Please help us spread the word by sharing the survey link on your social network feeds, at meetups, around your office, and in other communities.

If you have any questions, please see our frequently asked questions.

Finally, we wanted to thank everyone who helped develop, polish, and test the survey.

Continue Reading…

Rust Blog

Announcing Rust 1.65.0

The Rust team is happy to announce a new version of Rust, 1.65.0. Rust is a programming language empowering everyone to build reliable and efficient software.


Before going into the details of the new Rust release, we'd like to draw attention to the tragic death of Mahsa Amini and the death and violent suppression of many others, by the religious morality police of Iran. See https://en.wikipedia.org/wiki/Mahsa%5FAmini%5Fprotests for more details. We stand in solidarity with the people in Iran struggling for human rights.


If you have a previous version of Rust installed via rustup, you can get 1.65.0 with:

rustup update stable

If you don't have it already, you can getrustup from the appropriate page on our website, and check out the detailed release notes for 1.65.0on GitHub.

If you'd like to help us out by testing future releases, you might consider updating locally to use the beta channel (rustup default beta) or the nightly channel (rustup default nightly). Pleasereport any bugs you might come across!

What's in 1.65.0 stable

Generic associated types (GATs)

Lifetime, type, and const generics can now be defined on associated types, like so:

trait Foo {
    type Bar<'x>;
}

It's hard to put into few words just how useful these can be, so here are a few example traits, to get a sense of their power:

/// An `Iterator`-like trait that can borrow from `Self`
trait LendingIterator {
    type Item<'a> where Self: 'a;

    fn next<'a>(&'a mut self) -> Option<Self::Item<'a>>;
}

/// Can be implemented over smart pointers, like `Rc` or `Arc`,
/// in order to allow being generic over the pointer type
trait PointerFamily {
    type Pointer<T>: Deref<Target = T>;

    fn new<T>(value: T) -> Self::Pointer<T>;
}

/// Allows borrowing an array of items. Useful for
/// `NdArray`-like types that don't necessarily store
/// data contiguously.
trait BorrowArray<T> {
    type Array<'x, const N: usize> where Self: 'x;

    fn borrow_array<'a, const N: usize>(&'a self) -> Self::Array<'a, N>;
}

As you can see, GATs are quite versatile and enable a number of patterns that are not currently able to be written. For more information, check out the post announcing thepush for stabilizationpublished last year or thestabilization announcement postpublished last week. The former goes into a bit more depth of a couple of the examples above, while the latter talks about some of the known limitations of this stabilization.

More in depth reading can be found in the associated types section of the nightly referenceor the original RFC (which was initially opened over 6.5 years ago!).

let-else statements

This introduces a new type of let statement with a refutable pattern and a diverging else block that executes when that pattern doesn't match.

let PATTERN: TYPE = EXPRESSION else {
    DIVERGING_CODE;
};

Normal let statements can only use irrefutable patterns, statically known to always match. That pattern is often just a single variable binding, but may also unpack compound types like structs, tuples, and arrays. However, that was not usable for conditional matches, like pulling out a variant of an enum -- until now! With let-else, a refutable pattern can match and bind variables in the surrounding scope like a normal let, or else diverge (e.g. break,return, panic!) when the pattern doesn't match.

fn get_count_item(s: &str) -> (u64, &str) {
    let mut it = s.split(' ');
    let (Some(count_str), Some(item)) = (it.next(), it.next()) else {
        panic!("Can't segment count item pair: '{s}'");
    };
    let Ok(count) = u64::from_str(count_str) else {
        panic!("Can't parse integer: '{count_str}'");
    };
    (count, item)
}
assert_eq!(get_count_item("3 chairs"), (3, "chairs"));

The scope of name bindings is the main thing that makes this different frommatch or if let-else expressions. You could previously approximate these patterns with an unfortunate bit of repetition and an outer let:

    let (count_str, item) = match (it.next(), it.next()) {
        (Some(count_str), Some(item)) => (count_str, item),
        _ => panic!("Can't segment count item pair: '{s}'"),
    };
    let count = if let Ok(count) = u64::from_str(count_str) {
        count
    } else {
        panic!("Can't parse integer: '{count_str}'");
    };

break from labeled blocks

Plain block expressions can now be labeled as a break target, terminating that block early. This may sound a little like a goto statement, but it's not an arbitrary jump, only from within a block to its end. This was already possible with loop blocks, and you may have seen people write loops that always execute only once, just to get a labeled break.

Now there's a language feature specifically for that! Labeled break may also include an expression value, just as with loops, letting a multi-statement block have an early "return" value.

let result = 'block: {
    do_thing();
    if condition_not_met() {
        break 'block 1;
    }
    do_next_thing();
    if condition_not_met() {
        break 'block 2;
    }
    do_last_thing();
    3
};

Splitting Linux debuginfo

Back in Rust 1.51, the compiler team added support for split debug informationon macOS, and now this option is stable for use on Linux as well.

  • -Csplit-debuginfo=unpacked will split debuginfo out into multiple .dwoDWARF object files.
  • -Csplit-debuginfo=packed will produce a single .dwp DWARF package alongside your output binary with all the debuginfo packaged together.
  • -Csplit-debuginfo=off is still the default behavior, which includes DWARF data in .debug_* ELF sections of the objects and final binary.

Split DWARF lets the linker avoid processing the debuginfo (because it isn't in the object files being linked anymore), which can speed up link times!

Other targets now also accept -Csplit-debuginfo as a stable option with their platform-specific default value, but specifying other values is still unstable.

Stabilized APIs

The following methods and trait implementations are now stabilized:

Of particular note, the Backtrace API allows capturing a stack backtrace at any time, using the same platform-specific implementation that usually serves panic backtraces. This may be useful for adding runtime context to error types, for example.

These APIs are now usable in const contexts:

Compatibility notes

  • As the final step of the RLS deprecation, this release has replaced RLS with a small LSP server showing a deprecation warning, advising users to migrate to rust-analyzer.

Other changes

There are other changes in the Rust 1.65 release, including:

  • MIR inlining is now enabled for optimized compilations. This provides a 3-10% improvement in compiletimes for real world crates.
  • When scheduling builds, Cargo now sorts the queue of pending jobs to improve performance.

Check out everything that changed inRust,Cargo, and Clippy.

Contributors to 1.65.0

Many people came together to create Rust 1.65.0. We couldn't have done it without all of you.Thanks!

Continue Reading…

Rust Blog

Generic associated types to be stable in Rust 1.65

As of Rust 1.65, which is set to release on November 3rd, generic associated types (GATs) will be stable — over six and a half years after the original RFC was opened. This is truly a monumental achievement; however, as with a few of the other monumental features of Rust, like async or const generics, there are limitations in the initial stabilization that we plan to remove in the future.

The goal of this post is not to teach about GATs, but rather to briefly introduce them to any readers that might not know what they are and to enumerate a few of the limitations in initial stabilization that users are most likely to run into. More detailed information can be found in the RFC, in the GATs initiative repository, in the previous blog post during the start of the stabilization push, in the associated items section in the nightly reference, or in the open issues on Github for GATs

What are GATs

At its core, generic associated types allow you to have generics (type, lifetime, or const) on associated types. Note that this is really just rounding out the places where you can put generics: for example, you can already have generics on freestanding type aliases and on functions in traits. Now you can just have generics on type aliases in traits (which we just call associated types). Here's an example of what a trait with a GAT would look like:

trait LendingIterator {
    type Item<'a> where Self: 'a;

    fn next<'a>(&'a mut self) -> Self::Item<'a>;
}

Most of this should look familiar; this trait looks very similar to the Iterator trait from the standard library. Fundamentally, this version of the trait allows the next function to return an item that borrows from self. For more detail about the example, as well as some info on what that where Self: 'a is for, check out the push for stabilization post.

In general, GATs provide a foundational basis for a vast range of patterns and APIs. If you really want to get a feel for how many projects how been blocked on GATs being stable, go scroll through either the tracking issue: you will find numerous issues from other projects linking to those threads over the years saying something along the lines of "we want the API to look like X, but for that we need GATs" (or see this comment that has some of these put together already). If you're interested in how GATs enable a library to do zero-copy parsing, resulting in nearly a ten-fold performance increase, you might be interested in checking out a blog post on it by Niko Matsakis.

All in all, even if you won't need to use GATs directly, it's very possible that the libraries you use will use GATs either internally or publically for ergonomics, performance, or just because that's the only way the implementation works.

When GATs go wrong - a few current bugs and limitations

As alluded to before, this stabilization is not without its bugs and limitations. This is not atypical compared to prior large language features. We plan to fix these bugs and remove these limitations as part of ongoing efforts driven by the newly-formed types team. (Stayed tuned for more details in an official announcement soon!)

Here, we'll go over just a couple of the limitations that we've identified that users might run into.

Implied 'static requirement from higher-ranked trait bounds

Consider the following code:

trait LendingIterator {
    type Item<'a> where Self: 'a;
}

pub struct WindowsMut<'x, T> {
    slice: &'x [T],
}

impl<'x, T> LendingIterator for WindowsMut<'x, T> {
    type Item<'a> = &'a mut [T] where Self: 'a;
}

fn print_items<I>(iter: I)
where
    I: LendingIterator,
    for<'a> I::Item<'a>: Debug,
{ ... }

fn main() {
    let mut array = [0; 16];
    let slice = &mut array;
    let windows = WindowsMut { slice };
    print_items::<WindowsMut<'_, usize>>(windows);
}

Here, imagine we wanted to have a LendingIterator where the items are overlapping slices of an array. We also have a function print_items that prints every item of a LendingIterator, as long as they implement Debug. This all seems innocent enough, but the above code doesn't compile — even though it should. Without going into details here, the for<'a> I::Item<'a>: Debug currently implies that I::Item<'a> must outlive 'static.

This is not really a nice bug. And of all the ones we'll mention today, this will likely be the one that is most limiting, annoying, and tough to figure out. This pops up much more often with GATs, but can be found with code that doesn't use GATs at all. Unfortunately, fixing this requires some refactorings to the compiler that isn't a short-term project. It is on the horizon though. The good news is that, in the meantime, we are least working on improving the error message you get from this code. This is what it will look like in the upcoming stabilization:

error[E0597]: `array` does not live long enough
   |
   |     let slice = &mut array;
   |                 ^^^^^^^^^^ borrowed value does not live long enough
   |     let windows = WindowsMut { slice };
   |     print_items::<WindowsMut<'_, usize>>(windows);
   |     -------------------------------------------- argument requires that `array` is borrowed for `'static`
   | }
   | - `array` dropped here while still borrowed
   |
note: due to current limitations in the borrow checker, this implies a `'static` lifetime
   |
   |     for<'a> I::Item<'a>: Debug,
   |                          ^^^^

It's not perfect, but it's something. It might not cover all cases, but if have a for<'a> I::Item<'a>: Trait bound somewhere and get an error that says something doesn't live long enough, you might be running into this bug. We're actively working to fix this. However, this error doesn't actually come up as often as you might expect while reading this (from our experience), so we feel the feature is still immensely useful enough even with it around.

Traits with GATs are not object safe

So, this one is a simple one. Making traits with GATs object safe is going to take a little bit of design work for its implementation. To get an idea of the work left to do here, let's start with a bit of code that you could write on stable today:

fn takes_iter(_: &dyn Iterator) {}

Well, you can write this, but it doesn't compile:

error[E0191]: the value of the associated type `Item` (from trait `Iterator`) must be specified
 --> src/lib.rs:1:23
  |
1 | fn takes_iter(_: &dyn Iterator) {}
  |                       ^^^^^^^^ help: specify the associated type: `Iterator<Item = Type>`

For a trait object to be well-formed, it must specify a value for all associated types. For the same reason, we don't want to accept the following:

fn no_associated_type(_: &dyn LendingIterator) {}

However, GATs introduce an extra bit of complexity. Take this code:

fn not_fully_generic(_: &dyn LendingIterator<Item<'static> = &'static str>) {}

So, we've specified the value of the associated type for one value of of the Item's lifetime ('static), but not for any value, like this:

fn fully_generic(_: &dyn for<'a> LendingIterator<Item<'a> = &'a str>) {}

While we have a solid idea of how to implement requirement in some future iterations of the trait solver (that uses more logical formulations), implementing it in the current trait solver is more difficult. Thus, we've chosen to hold off on this for now.

The borrow checker isn't perfect and it shows

Keeping with the LendingIterator example, let's start by looking at two methods on Iterator: for_each and filter:

trait Iterator {
    type Item;

    fn for_each<F>(self, f: F)
    where
        Self: Sized,
        F: FnMut(Self::Item);
    
    fn filter<P>(self, predicate: P) -> Filter<Self, P>
    where
        Self: Sized,
        P: FnMut(&Self::Item) -> bool;
}

Both of these take a function as an argument. Closures are often used these. Now, let's look at the LendingIterator definitions:

trait LendingIterator {
    type Item<'a> where Self: 'a;

    fn for_each<F>(mut self, mut f: F)
    where
        Self: Sized,
        F: FnMut(Self::Item<'_>);

    fn filter<P>(self, predicate: P) -> Filter<Self, P>
    where
        Self: Sized,
        P: FnMut(&Self::Item<'_>) -> bool;
}

Looks simple enough, but if it really was, would it be here? Let's start by looking at what happens when we try to use for_each:

fn iterate<T, I: for<'a> LendingIterator<Item<'a> = &'a T>>(iter: I) {
    iter.for_each(|_: &T| {})
}

error: `I` does not live long enough
   |
   |     iter.for_each(|_: &T| {})
   |                   ^^^^^^^^^^

Well, that isn't great. Turns out, this is pretty closely related to the first limitation we talked about earlier, even though the borrow checker does play a role here.

On the other hand, let's look at something that's very clearly a borrow checker problem, by looking at an implementation of the Filter struct returned by the filter method:

impl<I: LendingIterator, P> LendingIterator for Filter<I, P>
where
    P: FnMut(&I::Item<'_>) -> bool, // <- the bound from above, a function
{
    type Item<'a> = I::Item<'a> where Self: 'a; // <- Use the underlying type

    fn next(&mut self) -> Option<I::Item<'_>> {
        // Loop through each item in the underlying `LendingIterator`...
        while let Some(item) = self.iter.next() {
            // ...check if the predicate holds for the item...
            if (self.predicate)(&item) {
                // ...and return it if it does
                return Some(item);
            }
        }
        // Return `None` when we're out of items
        return None;
    }
}

Again, the implementation here shouldn't seem surprising. We, of course, run into a borrow checker error:

error[E0499]: cannot borrow `self.iter` as mutable more than once at a time
  --> src/main.rs:28:32
   |
27 |     fn next(&mut self) -> Option<I::Item<'_>> {
   |             - let's call the lifetime of this reference `'1`
28 |         while let Some(item) = self.iter.next() {
   |                                ^^^^^^^^^^^^^^^^ `self.iter` was mutably borrowed here in the previous iteration of the loop
29 |             if (self.predicate)(&item) {
30 |                 return Some(item);
   |                        ---------- returning this value requires that `self.iter` is borrowed for `'1`

This is a known limitation in the current borrow checker and should be solved in some future iteration (like Polonius).

Non-local requirements for where clauses on GATs

The last limitation we'll talk about today is a bit different than the others; it's not a bug and it shouldn't prevent any programs from compiling. But it all comes back to that where Self: 'a clause you've seen in several parts of this post. As mentioned before, if you're interested in digging a bit into why that clause is required, see the push for stabilization post.

There is one not-so-ideal requirement about this clause: you must write it on the trait. Like with where clauses on functions, you cannot add clauses to associated types in impls that aren't there in the trait. However, if you didn't add this clause, a large set of potential impls of the trait would be disallowed.

To help users not fall into the pitfall of accidentally forgetting to add this (or similar clauses that end up with the same effect for a different set of generics), we've implemented a set of rules that must be followed for a trait with GATs to compile. Let's first look at the error without writing the clause:

trait LendingIterator {
    type Item<'a>;

    fn next<'a>(&'a mut self) -> Self::Item<'a>;
}

error: missing required bound on `Item`
 --> src/lib.rs:2:5
  |
2 |     type Item<'a>;
  |     ^^^^^^^^^^^^^-
  |                  |
  |                  help: add the required where clause: `where Self: 'a`
  |
  = note: this bound is currently required to ensure that impls have maximum flexibility
  = note: we are soliciting feedback, see issue #87479 <https://github.com/rust-lang/rust/issues/87479> for more information

This error should hopefully be helpful (you can even cargo fix it!). But, what exactly are these rules? Well, ultimately, they end up being somewhat simple: for methods that use the GAT, any bounds that can be proven must also be present on the GAT itself.

Okay, so how did we end up with the required Self: 'a bound. Well, let's take a look at the next method. It returns Self::Item<'a>, and we have an argument &'a mut self. We're getting a bit into the details of the Rust language, but because of that argument, we know that Self: 'a must hold. So, we require that bound.

We're requiring these bounds now to leave room in the future to potentially imply these automatically (and of course because it should help users write traits with GATs). They shouldn't interfere with any real use-cases, but if you do encounter a problem, check out the issue mentioned in the error above. And if you want to see a fairly comprehensive testing of different scenarios on what bounds are required and when, check out the relevant test file.

Conclusion

Hopefully the limitations brought up here and explanations thereof don't detract from overall excitement of GATs stabilization. Sure, these limitations do, well, limit the number of things you can do with GATs. However, we would not be stabilizing GATs if we didn't feel that GATs are not still very useful. Additionally, we wouldn't be stabilizing GATs if we didn't feel that the limitations weren't solvable (and in a backwards-compatible manner).

To conclude things, all the various people involved in getting this stabilization to happen deserve the utmost thanks. As said before, it's been 6.5 years coming and it couldn't have happened without everyone's support and dedication. Thanks all!

Continue Reading…

Rust Blog

Announcing Rust 1.64.0

The Rust team is happy to announce a new version of Rust, 1.64.0. Rust is a programming language empowering everyone to build reliable and efficient software.

If you have a previous version of Rust installed via rustup, you can get 1.64.0 with:

rustup update stable

If you don't have it already, you can getrustup from the appropriate page on our website, and check out the detailed release notes for 1.64.0on GitHub.

If you'd like to help us out by testing future releases, you might consider updating locally to use the beta channel (rustup default beta) or the nightly channel (rustup default nightly). Pleasereport any bugs you might come across!

What's in 1.64.0 stable

Enhancing .await with IntoFuture

Rust 1.64 stabilizes theIntoFuturetrait. IntoFuture is a trait similar toIntoIterator, but rather than supporting for ... in ... loops, IntoFuture changes how.await works. With IntoFuture, the .await keyword can await more than just futures; it can await anything which can be converted into a Future viaIntoFuture - which can help make your APIs more user-friendly!

Take for example a builder which constructs requests to some storage provider over the network:

pub struct Error { ... }
pub struct StorageResponse { ... }:
pub struct StorageRequest(bool);

impl StorageRequest {
    /// Create a new instance of `StorageRequest`.
    pub fn new() -> Self { ... }
    /// Decide whether debug mode should be enabled.
    pub fn set_debug(self, b: bool) -> Self { ... }
    /// Send the request and receive a response.
    pub async fn send(self) -> Result<StorageResponse, Error> { ... }
}

Typical usage would likely look something like this:

let response = StorageRequest::new()  // 1. create a new instance
    .set_debug(true)                  // 2. set some option
    .send()                           // 3. construct the future
    .await?;                          // 4. run the future + propagate errors

This is not bad, but we can do better here. Using IntoFuture we can combine_"construct the future"_ (line 3) and "run the future" (line 4) into a single step:

let response = StorageRequest::new()  // 1. create a new instance
    .set_debug(true)                  // 2. set some option
    .await?;                          // 3. construct + run the future + propagate errors

We can do this by implementing IntoFuture for StorageRequest. IntoFuturerequires us to have a named future we can return, which we can do by creating a "boxed future" and defining a type alias for it:

// First we must import some new types into the scope.
use std::pin::Pin;
use std::future::{Future, IntoFuture};

pub struct Error { ... }
pub struct StorageResponse { ... }
pub struct StorageRequest(bool);

impl StorageRequest {
    /// Create a new instance of `StorageRequest`.
    pub fn new() -> Self { ... }
    /// Decide whether debug mode should be enabled.
    pub fn set_debug(self, b: bool) -> Self { ... }
    /// Send the request and receive a response.
    pub async fn send(self) -> Result<StorageResponse, Error> { ... }
}

// The new implementations:
// 1. create a new named future type
// 2. implement `IntoFuture` for `StorageRequest`
pub type StorageRequestFuture = Pin<Box<dyn Future<Output = Result<StorageResponse, Error> + Send + 'static>>
impl IntoFuture for StorageRequest {
    type IntoFuture = StorageRequestFuture;
    type Output = <StorageRequestFuture as Future>::Output;
    fn into_future(self) -> Self::IntoFuture {
        Box::pin(self.send())
    }
}

This takes a bit more code to implement, but provides a simpler API for users.

In the future, the Rust Async WG hopes to simplify the creating new named futures by supporting impl Trait in type aliases (Type Alias Impl Trait or TAIT). This should make implementing IntoFuture easier by simplifying the type alias' signature, and make it more performant by removing the Box from the type alias.

C-compatible FFI types in core and alloc

When calling or being called by C ABIs, Rust code can use type aliases likec_uint or c_ulong to match the corresponding types from C on any target, without requiring target-specific code or conditionals.

Previously, these type aliases were only available in std, so code written for embedded targets and other scenarios that could only use core or alloccould not use these types.

Rust 1.64 now provides all of the c_* type aliases incore::ffi, as well ascore::ffi::CStr for working with C strings. Rust 1.64 also providesalloc::ffi::CStringfor working with owned C strings using only the alloc crate, rather than the full std library.

rust-analyzer is now available via rustup

rust-analyzer is now included as part of the collection of tools included with Rust. This makes it easier to download and access rust-analyzer, and makes it available on more platforms. It is available as a rustup component which can be installed with:

rustup component add rust-analyzer

At this time, to run the rustup-installed version, you need to invoke it this way:

rustup run rust-analyzer

The next release of rustup will provide a built-in proxy so that running the executable rust-analyzer will launch the appropriate version.

Most users should continue to use the releases provided by the rust-analyzer team (available on the rust-analyzer releases page), which are published more frequently. Users of the official VSCode extensionare not affected since it automatically downloads and updates releases in the background.

Cargo improvements: workspace inheritance and multi-target builds

When working with collections of related libraries or binary crates in one Cargo workspace, you can now avoid duplication of common field values between crates, such as common version numbers, repository URLs, or rust-version. This also helps keep these values in sync between crates when updating them. For more details, seeworkspace.package,workspace.dependencies, and "inheriting a dependency from a workspace".

When building for multiple targets, you can now pass multiple --targetoptions to cargo build, to build all of those targets at once. You can also setbuild.targetto an array of multiple targets in .cargo/config.toml to build for multiple targets by default.

Stabilized APIs

The following methods and trait implementations are now stabilized:

These types were previously stable in std::ffi, but are now also available incore and alloc:

These types were previously stable in std::os::raw, but are now also available in core::ffi and std::ffi:

We've stabilized some helpers for use with Poll, the low-level implementation underneath futures:

In the future, we hope to provide simpler APIs that require less use of low-level details like Poll and Pin, but in the meantime, these helpers make it easier to write such code.

These APIs are now usable in const contexts:

Compatibility notes

  • As previously announced,linux targets now require at least Linux kernel 3.2 (except for targets which already required a newer kernel), and linux-gnu targets now require glibc 2.17 (except for targets which already required a newer glibc).
  • Rust 1.64.0 changes the memory layout of Ipv4Addr, Ipv6Addr,SocketAddrV4 and SocketAddrV6 to be more compact and memory efficient. This internal representation was never exposed, but some crates relied on it anyway by using std::mem::transmute, resulting in invalid memory accesses. Such internal implementation details of the standard library are_never_ considered a stable interface. To limit the damage, we worked with the authors of all of the still-maintained crates doing so to release fixed versions, which have been out for more than a year. The vast majority of impacted users should be able to mitigate with a cargo update.
  • As part of the RLS deprecation, this is also the last release containing a copy of RLS. Starting from Rust 1.65.0, RLS will be replaced by a small LSP server showing the deprecation warning.

Other changes

There are other changes in the Rust 1.64 release, including:

  • Windows builds of the Rust compiler now use profile-guided optimization, providing performance improvements of 10-20% for compiling Rust code on Windows.
  • If you define a struct containing fields that are never used, rustc will warn about the unused fields. Now, in Rust 1.64, you can enable theunused_tuple_struct_fields lint to get the same warnings about unused fields in a tuple struct. In future versions, we plan to make this lint warn by default. Fields of type unit (()) do not produce this warning, to make it easier to migrate existing code without having to change tuple indices.

Check out everything that changed inRust,Cargo, and Clippy.

Contributors to 1.64.0

Many people came together to create Rust 1.64.0. We couldn't have done it without all of you.Thanks!

Continue Reading…

Rust Blog

Const Eval (Un)Safety Rules

In a recent Rust issue (#99923), a developer noted that the upcoming 1.64-beta version of Rust had started signalling errors on their crate,icu4x. The icu4x crate uses unsafe code during const evaluation.Const evaluation, or just "const-eval", runs at compile-time but produces values that may end up embedded in the final object code that executes at runtime.

Rust's const-eval system supports both safe and unsafe Rust, but the rules for what unsafe code is allowed to do during const-eval are even more strict than what is allowed for unsafe code at runtime. This post is going to go into detail about one of those rules.

(Note: If your const code does not use any unsafe blocks or call any const fnwith an unsafe block, then you do not need to worry about this!)

A new diagnostic to watch for

The problem, reduced over the course of the comment thread of #99923, is that certain static initialization expressions (see below) are defined as having undefined behavior (UB) at compile time (playground):

pub static FOO: () = unsafe {
    let illegal_ptr2int: usize = std::mem::transmute(&());
    let _copy = illegal_ptr2int;
};

(Many thanks to @eddyb for the minimal reproduction!)

The code above was accepted by Rust versions 1.63 and earlier, but in the Rust 1.64-beta, it now causes a compile time error with the following message:

error[E0080]: could not evaluate static initializer
 --> demo.rs:3:17
  |
3 |     let _copy = illegal_ptr2int;
  |                 ^^^^^^^^^^^^^^^ unable to turn pointer into raw bytes
  |
  = help: this code performed an operation that depends on the underlying bytes representing a pointer
  = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported

As the message says, this operation is not supported: the transmuteabove is trying to reinterpret the memory address &() as an integer of typeusize. The compiler cannot predict what memory address the () would be associated with at execution time, so it refuses to allow that reinterpretation.

When you write safe Rust, then the compiler is responsible for preventing undefined behavior. When you write any unsafe code (be it const or non-const), you are responsible for preventing UB, and during const-eval, the rules about what unsafe code has defined behavior are even more strict than the analogous rules governing Rust's runtime semantics. (In other words, more code is classified as "UB" than you may have otherwise realized.)

If you hit undefined behavior during const-eval, the Rust compiler will protect itself from adverse effects such as the undefined behavior leaking into the type system, but there are few guarantees other than that. For example, compile-time UB could lead to runtime UB. Furthermore, if you have UB at const-eval time, there is no guarantee that your code will be accepted from one compiler version to another.

What is new here

You might be thinking: "it used to be accepted; therefore, there must be some value for the memory address that the previous version of the compiler was using here."

But such reasoning would be based on an imprecise view of what the Rust compiler was doing here.

The const-eval machinery of the Rust compiler is built upon the MIR-interpreterMiri, which uses an abstract model of a hypothetical machine as the foundation for evaluating such expressions. This abstract model doesn't have to represent memory addresses as mere integers; in fact, to support Miri's fine-grained checking for UB, it uses a much richer datatype for the values that are held in the abstract memory store.

The details of Miri's value representation do not matter too much for our discussion here. We merely note that earlier versions of the compiler silently accepted expressions that seemed to transmute memory addresses into integers, copied them around, and then transmuted them back into addresses; but that was not what was acutally happening under the hood. Instead, what was happening was that the Miri values were passed around blindly (after all, the whole point of transmute is that it does no transformation on its input value, so it is a no-op in terms of its operational semantics).

The fact that it was passing a memory address into a context where you would expect there to always be an integer value would only be caught, if at all, at some later point.

For example, the const-eval machinery rejects code that attempts to embed the transmuted pointer into a value that could be used by runtime code, like so (playground):

pub static FOO: usize = unsafe {
    let illegal_ptr2int: usize = std::mem::transmute(&());
    illegal_ptr2int
};

Likewise, it rejects code that attempts to perform arithmetic on that non-integer value, like so (playground):

pub static FOO: () = unsafe {
    let illegal_ptr2int: usize = std::mem::transmute(&());
    let _incremented = illegal_ptr2int + 1;
};

Both of the latter two variants are rejected in stable Rust, and have been for as long as Rust has accepted pointer-to-integer conversions in static initializers (see e.g. Rust 1.52).

More similar than different

In fact, all of the examples provided above are exhibiting undefined behavior according to the semantics of Rust's const-eval system.

The first example with _copy was accepted in Rust versions 1.46 through 1.63 because of Miri implementation artifacts. Miri puts considerable effort into detecting UB, but does not catch all instances of it. Furthermore, by default, Miri's detection can be delayed to a point far after where the actual problematic expression is found.

But with nightly Rust, we can opt into extra checks for UB that Miri provides, by passing the unstable flag -Z extra-const-ub-checks. If we do that, then for_all_ of the above examples we get the same result:

error[E0080]: could not evaluate static initializer
 --> demo.rs:2:34
  |
2 |     let illegal_ptr2int: usize = std::mem::transmute(&());
  |                                  ^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into raw bytes
  |
  = help: this code performed an operation that depends on the underlying bytes representing a pointer
  = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported

The earlier examples had diagnostic output that put the blame in a misleading place. With the more precise checking -Z extra-const-ub-checks enabled, the compiler highlights the expression where we can first witness UB: the original transmute itself! (Which was stated at the outset of this post; here we are just pointing out that these tools can pinpoint the injection point more precisely.)

Why not have these extra const-ub checks on by default? Well, the checks introduce performance overhead upon Rust compilation time, and we do not know if that overhead can be made acceptable. (However, recent debateamong Miri developers indicates that the inherent cost here might not be as bad as they had originally thought. Perhaps a future version of the compiler will have these extra checks on by default.)

Change is hard

You might well be wondering at this point: "Wait, when is it okay to transmute a pointer to a usize during const evaluation?" And the answer is simple: "Never."

Transmuting a pointer to a usize during const-eval has always been undefined behavior, ever since const-eval added support fortransmute and union. You can read more about this in theconst_fn_transmute / const_fn_union stabilization report, specifically the subsection entitled "Pointer-integer-transmutes". (It is also mentioned in the documentation for transmute.)

Thus, we can see that the classification of the above examples as UB during const evaluation is not a new thing at all. The only change here was that Miri had some internal changes that made it start detecting the UB rather than silently ignoring it.

This means the Rust compiler has a shifting notion of what UB it will explicitly catch. We anticipated this: RFC 3016, "const UB", explicitlysays:

[...] there is no guarantee that UB is reliably detected during CTFE. This can change from compiler version to compiler version: CTFE code that causes UB could build fine with one compiler and fail to build with another. (This is in accordance with the general policy that unsound code is not subject to stability guarantees.)

Having said that: So much of Rust's success has been built around the trust that we have earned with our community. Yes, the project has always reserved the right to make breaking changes when resolving soundness bugs; but we have also strived to mitigate such breakage whenever feasible, via things likefuture-incompatible lints.

Today, with our current const-eval architecture layered atop Miri, it is not feasible to ensure that changes such as the one that injected issue#99923 go through a future-incompat warning cycle. The compiler team plans to keep our eye on issues in this space. If we see evidence that these kinds of changes do cause breakage to a non-trivial number of crates, then we will investigate further how we might smooth the transition path between compiler releases. However, we need to balance any such goal against the fact that Miri has very a limited set of developers: the researchers determining how to define the semantics of unsafe languages like Rust. We do not want to slow their work down!

What you can do for safety's sake

If you observe the could not evaluate static initializer message on your crate atop Rust 1.64, and it was compiling with previous versions of Rust, we want you to let us know: file an issue!

We have performed a crater run for the 1.64-beta and that did not find any other instances of this particular problem. If you can test compiling your crate atop the 1.64-beta before the stable release goes out on September 22nd, all the better! One easy way to try the beta is to use rustup's override shortand for it:

$ rustup update beta
$ cargo +beta build

As Rust's const-eval evolves, we may see another case like this arise again. If you want to defend against future instances of const-eval UB, we recommend that you set up a continuous integration service to invoke the nightly rustc with the unstable -Z extra-const-ub-checks flag on your code.

Want to help?

As you might imagine, a lot of us are pretty interested in questions such as "what should be undefined behavior?"

See for example Ralf Jung's excellent blog series on why pointers are complicated (parts I, II, III), which contain some of the details elided above about Miri's representation, and spell out reasons why you might want to be concerned about pointer-to-usize transmutes even _outside_of const-eval.

If you are interested in trying to help us figure out answers to those kinds of questions, please join us in the unsafe code guidelines zulip.

If you are interested in learning more about Miri, or contributing to it, you can say Hello in the miri zulip.

Conclusion

To sum it all up: When you write safe Rust, then the compiler is responsible for preventing undefined behavior. When you write any unsafe code, you are responsible for preventing undefined behavior. Rust's const-eval system has a stricter set of rules governing what unsafe code has defined behavior: specifically, reinterpreting (aka "transmuting") a pointer value as a usize is undefined behavior during const-eval. If you have undefined behavior at const-eval time, there is no guarantee that your code will be accepted from one compiler version to another.

The compiler team is hoping that issue #99923 is an exceptional fluke and that the 1.64 stable release will not encounter any other surprises related to the aforementioned change to the const-eval machinery.

But fluke or not, the issue provided excellent motivation to spend some time exploring facets of Rust's const-eval architecture, and the Miri interpreter that underlies it. We hope you enjoyed reading this as much as we did writing it.

Continue Reading…

Rust Blog

Security advisories for Cargo (CVE-2022-36113, CVE-2022-36114)

This is a cross-post of the official security advisory. The official advisory contains a signed version with our PGP key, as well.

The Rust Security Response WG was notified that Cargo did not prevent extracting some malformed packages downloaded from alternate registries. An attacker able to upload packages to an alternate registry could fill the filesystem or corrupt arbitary files when Cargo downloaded the package.

These issues have been assigned CVE-2022-36113 and CVE-2022-36114. The severity of these vulnerabilities is "low" for users of alternate registries. Users relying on crates.io are not affected.

Note that by design Cargo allows code execution at build time, due to build scripts and procedural macros. The vulnerabilities in this advisory allow performing a subset of the possible damage in a harder to track down way. Your dependencies must still be trusted if you want to be protected from attacks, as it's possible to perform the same attacks with build scripts and procedural macros.

Arbitrary file corruption (CVE-2022-36113)

After a package is downloaded, Cargo extracts its source code in the ~/.cargofolder on disk, making it available to the Rust projects it builds. To record when an extraction is successfull, Cargo writes "ok" to the .cargo-ok file at the root of the extracted source code once it extracted all the files.

It was discovered that Cargo allowed packages to contain a .cargo-ok symbolic link, which Cargo would extract. Then, when Cargo attempted to write "ok" into .cargo-ok, it would actually replace the first two bytes of the file the symlink pointed to with ok. This would allow an attacker to corrupt one file on the machine using Cargo to extract the package.

Disk space exaustion (CVE-2022-36114)

It was discovered that Cargo did not limit the amount of data extracted from compressed archives. An attacker could upload to an alternate registry a specially crafted package that extracts way more data than its size (also known as a "zip bomb"), exhausting the disk space on the machine using Cargo to download the package.

Affected versions

Both vulnerabilities are present in all versions of Cargo. Rust 1.64, to be released on September 22nd, will include fixes for both of them.

Since these vulnerabilities are just a more limited way to accomplish what a malicious build scripts or procedural macros can do, we decided not to publish Rust point releases backporting the security fix. Patch files for Rust 1.63.0 are available in the wg-security-response repository for people building their own toolchains.

Mitigations

We recommend users of alternate registries to excercise care in which package they download, by only including trusted dependencies in their projects. Please note that even with these vulnerabilities fixed, by design Cargo allows arbitrary code execution at build time thanks to build scripts and procedural macros: a malicious dependency will be able to cause damage regardless of these vulnerabilities.

crates.io implemented server-side checks to reject these kinds of packages years ago, and there are no packages on crates.io exploiting these vulnerabilities. crates.io users still need to excercise care in choosing their dependencies though, as the same concerns about build scripts and procedural macros apply here.

Acknowledgements

We want to thank Ori Hollander from JFrog Security Research for responsibly disclosing this to us according to the Rust security policy.

We also want to thank Josh Triplett for developing the fixes, Weihang Lo for developing the tests, and Pietro Albini for writing this advisory. The disclosure was coordinated by Pietro Albini and Josh Stone.

Continue Reading…

Rust Blog

Announcing Rust 1.63.0

The Rust team is happy to announce a new version of Rust, 1.63.0. Rust is a programming language empowering everyone to build reliable and efficient software.

If you have a previous version of Rust installed via rustup, you can get 1.63.0 with:

rustup update stable

If you don't have it already, you can get rustupfrom the appropriate page on our website, and check out thedetailed release notes for 1.63.0 on GitHub.

If you'd like to help us out by testing future releases, you might consider updating locally to use the beta channel (rustup default beta) or the nightly channel (rustup default nightly). Please report any bugs you might come across!

What's in 1.63.0 stable

Scoped threads

Rust code could launch new threads with std::thread::spawn since 1.0, but this function bounds its closure with 'static. Roughly, this means that threads currently must have ownership of any arguments passed into their closure; you can't pass borrowed data into a thread. In cases where the threads are expected to exit by the end of the function (by being join()'d), this isn't strictly necessary and can require workarounds like placing the data in an Arc.

Now, with 1.63.0, the standard library is adding scoped threads, which allow spawning a thread borrowing from the local stack frame. Thestd::thread::scope API provides the necessary guarantee that any spawned threads will have exited prior to itself returning, which allows for safely borrowing data. Here's an example:

let mut a = vec![1, 2, 3];
let mut x = 0;

std::thread::scope(|s| {
    s.spawn(|| {
        println!("hello from the first scoped thread");
        // We can borrow `a` here.
        dbg!(&a);
    });
    s.spawn(|| {
        println!("hello from the second scoped thread");
        // We can even mutably borrow `x` here,
        // because no other threads are using it.
        x += a[0] + a[2];
    });
    println!("hello from the main thread");
});

// After the scope, we can modify and access our variables again:
a.push(4);
assert_eq!(x, a.len());

Rust ownership for raw file descriptors/handles (I/O Safety)

Previously, Rust code working with platform APIs taking raw file descriptors (on unix-style platforms) or handles (on Windows) would typically work directly with a platform-specific representation of the descriptor (for example, a c_int, or the alias RawFd). For Rust bindings to such native APIs, the type system then failed to encode whether the API would take ownership of the file descriptor (e.g., close) or merely borrow it (e.g., dup).

Now, Rust provides wrapper types such as BorrowedFd and OwnedFd, which are marked as#[repr(transparent)], meaning that extern "C" bindings can directly take these types to encode the ownership semantics. See the stabilized APIs section for the full list of wrapper types stabilized in 1.63, currently, they are available on cfg(unix) platforms, Windows, and WASI.

We recommend that new APIs use these types instead of the previous type aliases (like RawFd).

const Mutex, RwLock, Condvar initialization

The Condvar::new, Mutex::new, and RwLock::new functions are now callable in const contexts, which allows avoiding the use of crates likelazy_static for creating global statics with Mutex, RwLock, or Condvarvalues. This builds on the work in 1.62 to enable thinner and faster mutexes on Linux.

Turbofish for generics in functions with impl Trait

For a function signature like fn foo<T>(value: T, f: impl Copy), it was an error to specify the concrete type of T via turbofish: foo::<u32>(3, 3)would fail with:

error[E0632]: cannot provide explicit generic arguments when `impl Trait` is used in argument position
 --> src/lib.rs:4:11
  |
4 |     foo::<u32>(3, 3);
  |           ^^^ explicit generic argument not allowed
  |
  = note: see issue #83701 <https://github.com/rust-lang/rust/issues/83701> for more information

In 1.63, this restriction is relaxed, and the explicit type of the generic can be specified. However, the impl Trait parameter, despite desugaring to a generic, remains opaque and cannot be specified via turbofish.

Non-lexical lifetimes migration complete

As detailed in this blog post, we've fully removed the previous lexical borrow checker from rustc across all editions, fully enabling the non-lexical, new, version of the borrow checker. Since the borrow checker doesn't affect the output of rustc, this won't change the behavior of any programs, but it completes a long-running migration (started in the initial stabilization of NLL for the 2018 edition) to deliver the full benefits of the new borrow checker across all editions of Rust. For most users, this change will bring slightly better diagnostics for some borrow checking errors, but will not otherwise impact which code they can write.

You can read more about non-lexical lifetimes in this section of the 2018 edition announcement.

Stabilized APIs

The following methods and trait implementations are now stabilized:

These APIs are now usable in const contexts:

Other changes

There are other changes in the Rust 1.63.0 release. Check out what changed inRust,Cargo, and Clippy.

Contributors to 1.63.0

Many people came together to create Rust 1.63.0. We couldn't have done it without all of you.Thanks!

Continue Reading…

Rust Blog

Non-lexical lifetimes (NLL) fully stable

As of Rust 1.63 (releasing next week), the "non-lexical lifetimes" (NLL) work will be enabled by default. NLL is the second iteration of Rust's borrow checker. The RFC actually does quite a nice job of highlighting some of the motivating examples. "But," I hear you saying, "wasn't NLL included in Rust 2018?" And yes, yes it was! But at that time, NLL was only enabled for Rust 2018 code, while Rust 2015 code ran in "migration mode". When in "migration mode," the compiler would run both the old and the new borrow checker and compare the results. This way, we could give warnings for older code that should never have compiled in the first place; we could also limit the impact of any bugs in the new code. Over time, we have limited migration mode to be closer and closer to just running the new-style borrow checker: in the next release, that process completes, and all Rust code will be checked with NLL.

How does removing the old borrow checker affect users?

At this point, we have almost completely merged "migration mode" and "regular mode", so switching to NLL will have very little impact on the user experience. A number of diagnostics changed, mostly for the better -- Jack Huey gives the full details in his blog post.

Credit where credit is due

The work to remove the old borrow checker has been going on for years. It's been a long, tedious, and largely thankless process. We'd like to take a moment to highlight the various people involved and make sure they are recognized for their hard work:

Jack's blog post includes a detailed narrative of all the work involved if you'd like more details! It's a fun read.

Looking forward: what can we expect for the "borrow checker of the future"?

The next frontier for Rust borrow checking is taking the polonius project and moving it from research experiment to production code. Polonius is a next-generation version of the borrow checker that was "spun off" from the main NLL effort in 2018, as we were getting NLL ready to ship in production. Its most important contribution is fixing a known limitation of the borrow checker, demonstrated by the following example:

fn last_or_push<'a>(vec: &'a mut Vec<String>) -> &'a String {
    if let Some(s) = vec.last() { // borrows vec
        // returning s here forces vec to be borrowed
        // for the rest of the function, even though it
        // shouldn't have to be
        return s; 
    }
    
    // Because vec is borrowed, this call to vec.push gives
    // an error!
    vec.push("".to_string()); // ERROR
    vec.last().unwrap()
}

This example doesn't compile today (try it for yourself), though there's not a good reason for that. You can often workaround the problem by editing the code to introduce a redundant if (as shown in this example), but with polonius, it will compile as is. If you'd like to learn more about how polonius (and the existing borrow checker) works1, you can watch my talk from Rust Belt Rust.

  1. Or where the name "polonius" comes from!

Continue Reading…

Rust Blog

Increasing the glibc and Linux kernel requirements

The minimum requirements for Rust toolchains targeting Linux will increase with the Rust 1.64.0 release (slated for September 22nd, 2022). The new minimum requirements are:

  • glibc >= 2.17 (previously glibc >= 2.11)
  • kernel >= 3.2 (previously kernel >= 2.6.32)

These requirements apply both to running the Rust compiler itself (and other Rust tooling like Cargo or Rustup), and to running binaries produced by Rust, if they use the libstd.

If you are not targeting an old long-term-support distribution, or embedded hardware running an old Linux version, this change is unlikely to affect you. Otherwise, read on!

Affected targets

In principle, the new kernel requirements affect all *-linux-* targets, while the glibc requirements affect all *-linux-gnu* targets. In practice, many targets were already requiring newer kernel or glibc versions. The requirements for such targets do not change.

Among targets for which a Rust host toolchain is distributed, the following are affected:

  • i686-unknown-linux-gnu (Tier 1)
  • x86_64-unknown-linux-gnu (Tier 1)
  • x86_64-unknown-linux-musl (Tier 2 with host tools)
  • powerpc-unknown-linux-gnu (Tier 2 with host tools)
  • powerpc64-unknown-linux-gnu (Tier 2 with host tools)
  • s390x-unknown-linux-gnu (Tier 2 with host tools)

The following are not affected, because they already had higher glibc/kernel requirements:

  • aarch64-unknown-linux-gnu (Tier 1)
  • aarch64-unknown-linux-musl (Tier 2 with host tools)
  • arm-unknown-linux-gnueabi (Tier 2 with host tools)
  • arm-unknown-linux-gnueabihf (Tier 2 with host tools)
  • armv7-unknown-linux-gnueabihf (Tier 2 with host tools)
  • mips-unknown-linux-gnueabihf (Tier 2 with host tools)
  • powerpc64le-unknown-linux-gnueabihf (Tier 2 with host tools)
  • riscv64gc-unknown-linux-gnueabihf (Tier 2 with host tools)

For other tier 2 or tier 3 targets, for which no Rust toolchain is distributed, we do not accurately track minimum requirements, and they may or may not be affected by this change.*-linux-musl* targets are only affected by the kernel requirements, not the glibc requirements. Targets which only use libcore and not libstd are unaffected.

A list of supported targets and their requirements can be found on theplatform support page. However, the page is not yet up to date with the changes announced here.

Affected systems

The glibc and kernel versions used for the new baseline requirements are already close to a decade old. As such, this change should only affect users that either target old long-term-support Linux distributions, or embedded hardware running old versions of Linux.

The following Linux distributions are still supported under the new requirements:

  • RHEL 7 (glibc 2.17, kernel 3.10)
  • SLES 12-SP5 (glibc 2.22, kernel 4.12.14)
  • Debian 8 (glibc 2.19, kernel 3.16.7)
  • Ubuntu 14.04 (glibc 2.19, kernel 3.13)

The following distributions are not supported under the new requirements:

  • RHEL 6 (glibc 2.12, kernel 2.6.32)
  • SLES 11-SP4 (glibc 2.11.3, kernel 3.0.101)
  • Debian 6 (glibc 2.11, kernel 2.6.32), Debian 7 (glibc 2.13, kernel 3.2.41)
  • Ubuntu 12.04 (glibc 2.15, kernel 3.2)

Out of the distributions in the second list, only RHEL 6 still has limited vendor support (ELS).

Why increase the requirements?

We want Rust, and binaries produced by Rust, to be as widely usable as possible. At the same time, the Rust project only has limited resources to maintain compatibility with old environments.

There are two parts to the toolchain requirements: The minimum requirements for running the Rust compiler on a host system, and the minimum requirements for cross-compiled binaries.

The minimum requirements for host toolchains affect our build system. Rust CI produces binary artifacts for dozens of different targets. Creating binaries that support old glibc versions requires either building on an operating system with old glibc (for native builds) or using a buildroot with an old glibc version (for cross-compiled builds).

At the same time, Rust relies on LLVM for optimization and code generation, which regularly increases its toolchain requirements. LLVM 16 will require GCC 7.1 or newer (and LLVM 15 supports GCC 5.1 in name only). Creating a build environment that has both a very old glibc and a recent compiler becomes increasingly hard over time. crosstool-ng (which we use for most cross-compilation needs) does not support targeting both glibc 2.11, and using a compiler that satisfies the new LLVM requirements.

The requirements for cross-compiled binaries have a different motivation: They affect which kernel versions need to be supported by libstd. Increasing the kernel requirements allows libstd to use newer syscalls, without having to maintain and test compatibility with kernels that do not support them.

The new baseline requirements were picked as the least common denominator among long-term-support distributions that still have active support. This is currently RHEL 7 with glibc 2.17 and kernel 3.10. The kernel requirement is picked as 3.2 instead, because this is the minimum requirement of glibc itself, and there is little relevant API difference between these versions.

What should I do?

If you or your organization are affected by this change, there are a number of viable options depending on your situation:

  • Upgrade your target system, or raise the minimum requirements of your software, to satisfy the new constraints.
  • If you are running the Rust compiler on an old host, consider cross-compiling from a newer host instead.
  • If you are targeting an old glibc version, consider targeting musl instead.
  • If you are targeting an old kernel version and use libstd, you may be out of luck: In this case you may have to either freeze your current Rust version, or maintain a fork of libstd that supports older kernels.

Continue Reading…

Rust Blog

Announcing Rust 1.62.1

The Rust team has published a new point release of Rust, 1.62.1. Rust is a programming language that is empowering everyone to build reliable and efficient software.

If you have a previous version of Rust installed via rustup, you can get 1.62.1 with:

rustup update stable

If you don't have it already, you can get rustupfrom the appropriate page on our website, and check out thedetailed release notes for 1.62.1 on GitHub.

What's in 1.62.1 stable

Rust 1.62.1 addresses a few recent regressions in the compiler and standard library, and also mitigates a CPU vulnerability on Intel SGX.

Contributors to 1.62.1

Many people came together to create Rust 1.62.1. We couldn't have done it without all of you. Thanks!

Continue Reading…

Rust Blog

Changes in the Core Team

We want to say farewell and thanks to a couple of people who are stepping back from the Core Team:

  • Aidan Hobson Sayers is leaving the Core Team and other roles. Joining the Core Team in 2018, he started out on the project with fixing CI issues and has been a member of the Infrastructure Team since its inception (moving through lead and co-lead). Aidan wants to dedicate more time to working with the Rust community and growing Rust usage at his company, and is looking forward to doing so from the other side of the fence.
  • Ashley Williams will be stepping down from the Core Team and other roles. She became a member of the Core Team in 2018 and has had impact on many parts of the project, from leading the Community team, to setting up PagerDuty for Infrastructure and Crates.io, to the Wasm working group, to Increasing Rust’s Reach and Rustbridge, to her instrumental work creating the Rust Foundation and serving as its first Executive Director. Ashley is leaving her role in the project to focus on her newly founded company.

Many thanks to both of them for their contributions and we look forward to seeing their future efforts with Rust!

Continue Reading…

Rust Blog

Announcing Rustup 1.25.1

The rustup working group is announcing the release of rustup version 1.25.1.Rustup is the recommended tool to install Rust, a programming language that is empowering everyone to build reliable and efficient software.

If you have a previous version of rustup installed, getting rustup 1.25.1 is as easy as stopping any programs which may be using Rustup (e.g. closing your IDE) and running:

rustup self update

Rustup will also automatically update itself at the end of a normal toolchain update:

rustup update

If you don't have it already, you can get rustup from the appropriate page on our website.

What's new in rustup 1.25.1

This version of rustup fixes a regression introduced in the previous release (1.25.0), which caused some workflows to fail.

Regression in nested Cargo invocations with different toolchains

When you invoke Rust or Cargo installed by rustup, you're not running them directly. Instead, you run rustup "proxy" binaries, whose job is to detect the right toolchain (parsing the +channel CLI argument or using one of the defaults) and run it.

Running these proxies is not instantaneous though, and for example a cargo build invocation might execute several of them (the initial cargo invocation plus one rustc for every dependency), slowing down the build.

To improve performance, rustup 1.25.0 changed the proxies code to set theRUSTC and RUSTDOC environment variables when missing, which instructed Cargo to skip the proxies and invoke the binaries defined in those variables directly. This provided a performance gain when building crates with lots of dependencies.

Unfortunately this change broke some users of rustup, who did something like:

  • The first Cargo invocation (for example an extension, an alias or an integration test) uses toolchain foo, setting the RUSTC and RUSTDOCenvironment variables pointing to that toolchain.
  • The first invocation calls Cargo again, but this time using toolchain bar(for example cargo +bar build). This does not set the RUSTC andRUSTDOC environment variables pointing to bar, as those variables are already present.
  • The second invocation of Cargo then invokes rustc by using the RUSTCenvironment variable and skipping the proxy, which results in the footoolchain being invoked. Previous versions of rustup invoked the proxy instead, which would correctly detect and use the bar toolchain.

Rustup 1.25.1 fixes this regression by reverting the change. The rustup working group is discussing in issue #3035 plans to re-introduce the change in a future release while avoiding breakage.

Thanks

Thanks again to all the contributors who made rustup 1.25.1 possible!

  • Daniel Silverstone (kinnison)
  • Robert Collins (rbtcollins)
  • Joshua Nelson (jyn514)
  • Pietro Albini (pietroalbini)

Continue Reading…

Rust Blog

Announcing Rustup 1.25.0

The rustup working group is happy to announce the release of rustup version 1.25.0. Rustup is the recommended tool to install Rust, a programming language that is empowering everyone to build reliable and efficient software.

If you have a previous version of rustup installed, getting rustup 1.25.0 is as easy as stopping any programs which may be using Rustup (e.g. closing your IDE) and running:

rustup self update

Rustup will also automatically update itself at the end of a normal toolchain update:

rustup update

If you don't have it already, you can get rustup from the appropriate page on our website.

What's new in rustup 1.25.0

This version of Rustup involves a significant number of internal cleanups, both in terms of the Rustup code and its documentation. In addition to a lot of work on the codebase itself, due to the length of time since the last release this one has a record number of contributors and we thank you all for your efforts and time.

One of the biggest changes in 1.25.0 is the new offer on Windows installs to auto-install the Visual Studio 2022 compilers which should simplify the process of getting started for people not used to developing on Windows with the MSVC-compatible toolchains.

A second important change for 1.25.0 is a number of PRs focussed around startup performance for Rustup. While it may not seem all that important to many, Rustup's startup time is a factor in the time it takes to do builds which involve large numbers of crates on systems which do not have large numbers of CPU cores. Hopefully the people for whom this is a common activity will notice an improvement; though there's more opportunity to speed things up still available.

Some, but by no means all, of the rest of this release's highlights includes support forrustup default none to unset the default toolchain, support for Windows arm64, inclusion of rust-gdbgui as a proxy so that platforms which support it can use GDB's gui mode with Rust, and some improvements to rustup-init.sh.

Full details are available in the changelog!

Rustup's documentation is also available in the rustup book.

Thanks

Thanks again to all the contributors who made rustup 1.25.0 possible!

  • 二手掉包工程师 (hi-rustin)
  • Brian Bowman (Seeker14491)
  • Jon Gjengset (jonho)
  • pierwill
  • Daniel Silverstone (kinnison)
  • Robert Collins (rbtcollins)
  • Alan Somers (asomers)
  • Brennan Vincent (umanwizard)
  • Joshua Nelson (jyn514)
  • Eric Huss (ehuss)
  • Will Bush (willbush)
  • Thad Guidry (thadguidry)
  • Alexander Lovchin (alovchin91)
  • zoodirector
  • Takayuki Nakata (giraffate)
  • Yusuke Abe (chansuke)
  • Wyatt Carss (wcarss)
  • Sondre Aasemoen (sondr3)
  • facklambda
  • Chad Dougherty (crd477)
  • Noritada Kobayashi (noritada)
  • Milan (mdaverde)
  • Pat Sier (pjsier)
  • Matt Keeter (mkeeter)
  • Alex Macleod (alexendoo)
  • Sathwik Matsa (sathwikmatsa)
  • Kushal Das (kushaldas)
  • Justus Winter (teythoon)
  • k900
  • Nicolas Ambram (nico-abram)
  • Connor Slade (basicprogrammer10)
  • Yerkebulan Tulibergenov (yerke)
  • Caleb Cartwright (calebcartwright)
  • Matthias Beyer (matthiasbeyer)
  • spacemaniac
  • Alex Touchet (atouchet)
  • Guillaume Gomez (guillaumegomez)
  • Chris Denton (chrisdenton)
  • Thomas Orozco (krallin)
  • cui fliter (cuishuang)
  • Martin Nordholts (enselic)
  • Emil Gardström (emilgardis)
  • Arlo Siemsen (arlosi)

Continue Reading…

Rust Blog

RLS Deprecation

The Rust Language Server (RLS) is being deprecated in favor of rust-analyzer. Current users of RLS should migrate to using rust-analyzer instead. Builds of RLS will continue to be released until at least the Rust 1.64 release (2022-09-22), after which no new releases will be made. This timeline may change if any issues arise.

RLS is an implementation of the Language Server Protocol (LSP) which provides enhanced features with any editor that supports the protocol, such as code-checking and refactoring. RLS was introduced by RFC 1317 and development was very active from 2016 through 2019. However, the architecture of RLS has several limitations that can make it difficult to provide low-latency and high-quality responses needed for an interactive environment.

Development of rust-analyzer began near the beginning of 2018 to provide an alternate LSP implementation for Rust. rust-analyzer uses a fundamentally different approach that does not rely on using rustc. In RFC 2912 rust-analyzer was adopted as the official replacement for RLS.

How you migrate to rust-analyzer will depend on which editor you are using. If you are using VSCode, you should uninstall the rust-lang.rust extension and install the official rust-lang.rust-analyzer extension. For other editors, please consult the rust-analyzer manual for instructions on how to install it.

Should you have any issues migrating to rust-analyzer, the Editors and IDEs category on the Rust Users forum is available for help with installation and usage.

We will soon be marking the official rust-lang.rust VSCode extension as deprecated, and will be implementing notifications that will inform users about the transition. After the end of release builds of RLS, we plan to replace the rls executable in official Rust releases with a small LSP implementation that informs the user that RLS is no longer available.

We would like to thank everyone who has worked on RLS and rust-analyzer. These options would not exist without the tremendous effort of all the contributors to these projects.

Continue Reading…

Rust Blog

Announcing Rust 1.62.0

The Rust team is happy to announce a new version of Rust, 1.62.0. Rust is a programming language empowering everyone to build reliable and efficient software.

If you have a previous version of Rust installed via rustup, you can get 1.62.0 with:

rustup update stable

If you don't have it already, you can get rustupfrom the appropriate page on our website, and check out thedetailed release notes for 1.62.0 on GitHub.

If you'd like to help us out by testing future releases, you might consider updating locally to use the beta channel (rustup default beta) or the nightly channel (rustup default nightly). Please report any bugs you might come across!

What's in 1.62.0 stable

cargo add

You can now add new dependencies directly from the command line using cargo add. This command supports specifying features and versions. It can also be used to modify existing dependencies.

For example:

cargo add log
cargo add serde --features derive
cargo add nom@5

See the cargo documentation for more.

#[default] enum variants

You can now use #[derive(Default)] on enums if you specify a default variant. For example, until now you would have to manually write a Default impl for this enum:

#[derive(Default)]
enum Maybe<T> {
    #[default]
    Nothing,

    Something(T),
}

As of now only "unit" variants (variants that have no fields) are allowed to be marked #[default]. More information is available in the RFC for this feature.

Thinner, faster mutexes on Linux

Previously, Mutex, Condvar, and RwLock were backed by the pthreads library on Linux. The pthreads locks support more features than the Rust APIs themselves do, including runtime configuration, and are designed to be used in languages with fewer static guarantees than Rust provides.

The mutex implementation, for example, is 40 bytes and cannot be moved. This forced the standard library to allocate a Box behind the scenes for each new mutex for platforms that use pthreads.

Rust's standard library now ships with a raw futex-based implementation of these locks on Linux, which is very lightweight and doesn't require extra allocation. In 1.62.0 Mutex only needs 5 bytes for its internal state on Linux, though this may change in future versions.

This is part of a long effort to improve the efficiency of Rust's lock types, which includes previous improvements on Windows such as unboxing its primitives. You can read more about that effort in the tracking issue.

Bare metal x86_64 target

It's now easier to build OS-less binaries for x86_64, for example when writing a kernel. The x86_64-unknown-none target has been promoted to Tier 2 and can be installed with rustup.

rustup target add x86_64-unknown-none
rustc --target x86_64-unknown-none my_no_std_program.rs

You can read more about development using no_std in the Embedded Rust book.

Stabilized APIs

The following methods and trait implementations are now stabilized:

Other changes

There are other changes in the Rust 1.62.0 release. Check out what changed inRust,Cargo, and Clippy.

Contributors to 1.62.0

Many people came together to create Rust 1.62.0. We couldn't have done it without all of you.Thanks!

Continue Reading…

Rust Blog

Announcing The RustConf PostConf UnConf

Hello Rust community!

We're excited to announce that the Rust project teams will be hosting an unconference1 the day after RustConf.

RustConf PostConf UnConf promo image

The PostConf Unconf will be dedicated to the Rust project and will be a fantastic opportunity for users, contributors, and maintainers to network and discuss the project's development.

There will be no set agenda; instead, attendees will decide what will be discussed together and can move freely between sessions to find ones in which they can contribute most effectively based on their individual interests and needs.

To get the most out of the unconference, jot down your thoughts ahead of time and bring them ready to share. We will also set up a channel in the RustConf Discord for folks to communicate and make preliminary, informal plans.

If you plan to attend, please register as soon as possible to help us plan appropriately. If space is limited, project participants and conference attendees will be given preference. Registration is free and open to everyone, but we require either a RustConf registration ID or a $10 deposit at signup to ensure that registrations are an accurate approximation of participants.

🚨Register Now!🚨

We hope to see you there!

  1. If you are unfamiliar with the notion of an unconference, you may find this informational piece helpful.

Continue Reading…

Rust Blog

Call for testing: Cargo sparse-registry

The Cargo nightly sparse-registry feature is ready for testing. The feature causes Cargo to access the crates.io index over HTTP, rather than git. It can provide a significant performance improvement, especially if the local copy of the git index is out-of-date or not yet cloned.

Overview

To try it out, add the -Z sparse-registry flag on a recent nightly build of Cargo. For example, to update dependencies:

rustup update nightly
cargo +nightly -Z sparse-registry update

The feature can also be enabled by setting the environment variableCARGO_UNSTABLE_SPARSE_REGISTRY=true. Setting this variable will have no effect on stable Cargo, making it easy to opt-in for CI jobs.

The minimum Cargo version is cargo 2022-06-17, which is bundled with rustc 2022-06-20.

You can leave feedback on the internals thread.

If you see any issues please report them on the Cargo repo. The output of Cargo with the environment variable CARGO_LOG=cargo::sources::registry::http_remote=trace set will be helpful in debugging.

Details

Accessing the index over HTTP allows crates.io to continue growing without hampering performance. The current git index continues to grow as new crates are published, and clients must download the entire index. The HTTP index only requires downloading metadata for crates in your dependency tree.

The performance improvement for clients should be especially noticeable in CI environments, particularly if no local cache of the index exists.

On the server side, the HTTP protocol is much simpler to cache on a CDN, which improves scalability and reduces server load. Due to this caching, crate updates may take an extra minute to appear in the index.

The Cargo team plans to eventually make this the default way to access crates.io (though the git index will remain for compatibility with older versions of Cargo and external tools). Cargo.lock files will continue to reference the existing crates.io index on GitHub to avoid churn.

The -Z sparse-registry flag also enables alternative registries to be accessed over HTTP. For more details, see the tracking issue.

Thank you

This project has been in the works for over 2.5 years with collaboration from the crates.io, infra, and Cargo teams.

@kornelski wrote the sparse-index RFC and initial performance proof of concept. @jonhoo created the initial implementation in Cargo and gathered performance data. @arlosicompleted the implementation in Cargo and implemented the changes to crates.io to serve the index. @eh2406 provided numerous reviews and feedback to get all the changes landed. Many others from the community helped by providing suggestions, feedback, and testing.

Thank you to everyone involved!

Continue Reading…

Rust Blog

Announcing Rust 1.61.0

The Rust team is happy to announce a new version of Rust, 1.61.0. Rust is a programming language empowering everyone to build reliable and efficient software.

If you have a previous version of Rust installed via rustup, you can get 1.61.0 with:

rustup update stable

If you don't have it already, you can get rustupfrom the appropriate page on our website, and check out thedetailed release notes for 1.61.0 on GitHub.

If you'd like to help us out by testing future releases, you might consider updating locally to use the beta channel (rustup default beta) or the nightly channel (rustup default nightly). Please report any bugs you might come across!

What's in 1.61.0 stable

Custom exit codes from main

In the beginning, Rust main functions could only return the unit type () (either implicitly or explicitly), always indicating success in the exit status, and if you wanted otherwise you had to call process::exit(code). Since Rust 1.26, main has been allowed to return a Result, where Ok translated to a C EXIT_SUCCESS andErr to EXIT_FAILURE (also debug-printing the error). Under the hood, these alternate return types were unified by an unstable Termination trait.

In this release, that Termination trait is finally stable, along with a more general ExitCodetype that wraps platform-specific return types. That has SUCCESS and FAILURE constants, and also implements From<u8> for more arbitrary values. The Termination trait can also be implemented for your own types, allowing you to customize any kind of reporting before converting to an ExitCode.

For example, here's a type-safe way to write exit codes for a git bisect run script:

use std::process::{ExitCode, Termination};

#[repr(u8)]
pub enum GitBisectResult {
    Good = 0,
    Bad = 1,
    Skip = 125,
    Abort = 255,
}

impl Termination for GitBisectResult {
    fn report(self) -> ExitCode {
        // Maybe print a message here
        ExitCode::from(self as u8)
    }
}

fn main() -> GitBisectResult {
    std::panic::catch_unwind(|| {
        todo!("test the commit")
    }).unwrap_or(GitBisectResult::Abort)
}

More capabilities for const fn

Several incremental features have been stabilized in this release to enable more functionality inconst functions:

  • Basic handling of fn pointers: You can now create, pass, and cast function pointers in aconst fn. For example, this could be useful to build compile-time function tables for an interpreter. However, it is still not permitted to call fn pointers.
  • Trait bounds: You can now write trait bounds on generic parameters to const fn, such asT: Copy, where previously only Sized was allowed.
  • dyn Trait types: Similarly, const fn can now deal with trait objects, dyn Trait.
  • impl Trait types: Arguments and return values for const fn can now be opaque impl Traittypes.

Note that the trait features do not yet support calling methods from those traits in a const fn.

See the Constant Evaluation section of the reference book to learn more about the current capabilities of const contexts, and future capabilities can be tracked in rust#57563.

Static handles for locked stdio

The three standard I/O streams -- Stdin, Stdout, and Stderr -- each have a lock(&self) to allow more control over synchronizing read and writes. However, they returned lock guards with a lifetime borrowed from &self, so they were limited to the scope of the original handle. This was determined to be an unnecessary limitation, since the underlying locks were actually in static storage, so now the guards are returned with a 'static lifetime, disconnected from the handle.

For example, a common error came from trying to get a handle and lock it in one statement:

// error[E0716]: temporary value dropped while borrowed
let out = std::io::stdout().lock();
//        ^^^^^^^^^^^^^^^^^       - temporary value is freed at the end of this statement
//        |
//        creates a temporary which is freed while still in use

Now the lock guard is 'static, not borrowing from that temporary, so this works!

Stabilized APIs

The following methods and trait implementations are now stabilized:

The following previously stable functions are now const:

Other changes

There are other changes in the Rust 1.61.0 release. Check out what changed inRust,Cargo, and Clippy.

In a future release we're planning to increase the baseline requirements for the Linux kernel to version 3.2, and for glibc to version 2.17. We'd love your feedback in rust#95026.

Contributors to 1.61.0

Many people came together to create Rust 1.61.0. We couldn't have done it without all of you.Thanks!

Continue Reading…

Rust Blog

Security advisory: malicious crate rustdecimal

This is a cross-post of the official security advisory. The official advisory contains a signed version with our PGP key, as well.

The Rust Security Response WG and the crates.io team were notified on 2022-05-02 of the existence of the malicious crate rustdecimal, which contained malware. The crate name was intentionally similar to the name of the popular rust_decimal crate, hoping that potential victims would misspell its name (an attack called "typosquatting").

To protect the security of the ecosystem, the crates.io team permanently removed the crate from the registry as soon as it was made aware of the malware. An analysis of all the crates on crates.io was also performed, and no other crate with similar code patterns was found.

Keep in mind that the rust_decimal crate was not compromised, and it is still safe to use.

Analysis of the crate

The crate had less than 500 downloads since its first release on 2022-03-25, and no crates on the crates.io registry depended on it.

The crate contained identical source code and functionality as the legitrust_decimal crate, except for the Decimal::new function.

When the function was called, it checked whether the GITLAB_CI environment variable was set, and if so it downloaded a binary payload into/tmp/git-updater.bin and executed it. The binary payload supported both Linux and macOS, but not Windows.

An analysis of the binary payload was not possible, as the download URL didn't work anymore when the analysis was performed.

Recommendations

If your project or organization is running GitLab CI, we strongly recommend checking whether your project or one of its dependencies depended on therustdecimal crate, starting from 2022-03-25. If you notice a dependency on that crate, you should consider your CI environment to be compromised.

In general, we recommend regularly auditing your dependencies, and only depending on crates whose author you trust. If you notice any suspicious behavior in a crate's source code please follow the Rust security policy and report it to the Rust Security Response WG.

Acknowledgements

We want to thank GitHub user @safinaskar for identifying the malicious crate in this GitHub issue.

Continue Reading…

Rust Blog

Announcing Rust 1.60.0

The Rust team is happy to announce a new version of Rust, 1.60.0. Rust is a programming language empowering everyone to build reliable and efficient software.

If you have a previous version of Rust installed via rustup, you can get 1.60.0 with:

rustup update stable

If you don't have it already, you can get rustupfrom the appropriate page on our website, and check out thedetailed release notes for 1.60.0 on GitHub. If you'd like to help us out by testing future releases, you might consider updating locally to use the beta channel (rustup default beta) or the nightly channel (rustup default nightly). Please report any bugs you might come across!

What's in 1.60.0 stable

Source-based Code Coverage

Support for LLVM-based coverage instrumentation has been stabilized in rustc. You can try this out on your code by rebuilding your code with -Cinstrument-coverage, for example like this:

RUSTFLAGS=&quot;-C instrument-coverage&quot; cargo build

After that, you can run the resulting binary, which will produce adefault.profraw file in the current directory. (The path and filename can be overriden by an environment variable; seedocumentationfor details).

The llvm-tools-preview component includes llvm-profdata for processing and merging raw profile output (coverage region execution counts); and llvm-covfor report generation. llvm-cov combines the processed output, fromllvm-profdata, and the binary itself, because the binary embeds a mapping from counters to actual source code regions.

rustup component add llvm-tools-preview
$(rustc --print sysroot)/lib/rustlib/x86_64-unknown-linux-gnu/bin/llvm-profdata merge -sparse default.profraw -o default.profdata
$(rustc --print sysroot)/lib/rustlib/x86_64-unknown-linux-gnu/bin/llvm-cov show -Xdemangler=rustfilt target/debug/coverage-testing \
    -instr-profile=default.profdata \
    -show-line-counts-or-regions \
    -show-instantiations

The above commands on a simple helloworld binary produce this annotated report, showing that each line of the input was covered.

 1|      1|fn main() {
    2|      1|    println!(&quot;Hello, world!&quot;);
    3|      1|}

For more details, please read thedocumentation in the rustc book. The baseline functionality is stable and will exist in some form in all future Rust releases, but the specific output format and LLVM tooling which produces it are subject to change. For this reason, it is important to make sure that you use the same version for both the llvm-tools-preview and the rustc binary used to compile your code.

cargo --timings

Cargo has stabilized support for collecting information on build with the --timings flag.

$ cargo build --timings
   Compiling hello-world v0.1.0 (hello-world)
      Timing report saved to target/cargo-timings/cargo-timing-20220318T174818Z.html
    Finished dev [unoptimized + debuginfo] target(s) in 0.98s

The report is also copied to target/cargo-timings/cargo-timing.html. A report on the release build of Cargo has been put up here. These reports can be useful for improving build performance. More information about the timing reports may be found in the documentation.

New syntax for Cargo features

This release introduces two new changes to improve support for Cargo features and how they interact with optional dependencies: Namespaced dependencies and weak dependency features.

Cargo has long supported features along with optional dependencies, as illustrated by the snippet below.

[dependencies]
jpeg-decoder = { version = &quot;0.1.20&quot;, default-features = false, optional = true }

[features]
# Enables parallel processing support by enabling the &quot;rayon&quot; feature of jpeg-decoder.
parallel = [&quot;jpeg-decoder/rayon&quot;]

There are two things to note in this example:

  • The optional dependency jpeg-decoder implicitly defines a feature of the same name. Enabling the jpeg-decoder feature will enable the jpeg-decoder dependency.
  • The &quot;jpeg-decoder/rayon&quot; syntax enables the jpeg-decoder dependency and enables the jpeg-decoder dependency's rayon feature.

Namespaced features tackles the first issue. You can now use the dep: prefix in the [features] table to explicitly refer to an optional dependency without implicitly exposing it as a feature. This gives you more control on how to define the feature corresponding to the optional dependency including hiding optional dependencies behind more descriptive feature names.

Weak dependency features tackle the second issue where the &quot;optional-dependency/feature-name&quot; syntax would always enable optional-dependency. However, often you want to enable the feature on the optional dependency only if some other feature has enabled the optional dependency. Starting in 1.60, you can add a ? as in &quot;package-name?/feature-name&quot; which will only enable the given feature if something else has enabled the optional dependency.

For example, let's say we have added some serialization support to our library, and it requires enabling a corresponding feature in some optional dependencies. That can be done like this:

[dependencies]
serde = { version = &quot;1.0.133&quot;, optional = true }
rgb = { version = &quot;0.8.25&quot;, optional = true }

[features]
serde = [&quot;dep:serde&quot;, &quot;rgb?/serde&quot;]

In this example, enabling the serde feature will enable the serde dependency. It will also enable the serde feature for the rgb dependency, but only if something else has enabled the rgb dependency.

Incremental compilation status

Incremental compilation is re-enabled for the 1.60 release. The Rust team continues to work on fixing bugs in incremental, but no problems causing widespread breakage are known at this time, so we have chosen to reenable incremental compilation. Additionally, the compiler team is continuing to work on long-term strategy to avoid future problems of this kind. That process is in relatively early days, so we don't have anything to share yet on that front.

Instant monotonicity guarantees

On all platforms Instant will try to use an OS API that guarantees monotonic behavior if available (which is the case on all tier 1 platforms). In practice such guarantees are -- under rare circumstances -- broken by hardware, virtualization, or operating system bugs. To work around these bugs and platforms not offering monotonic clocks, Instant::duration_since, Instant::elapsed andInstant::sub now saturate to zero. In older Rust versions this led to a panic instead. Instant::checked_duration_since can be used to detect and handle situations where monotonicity is violated, or Instants are subtracted in the wrong order.

This workaround obscures programming errors where earlier and later instants are accidentally swapped. For this reason future Rust versions may reintroduce panics in at least those cases, if possible and efficient.

Prior to 1.60, the monotonicity guarantees were provided through mutexes or atomics in std, which can introduce large performance overheads toInstant::now(). Additionally, the panicking behavior meant that Rust software could panic in a subset of environments, which was largely undesirable, as the authors of that software may not be able to fix or upgrade the operating system, hardware, or virtualization system they are running on. Further, introducing unexpected panics into these environments made Rust software less reliable and portable, which is of higher concern than exposing typically uninteresting platform bugs in monotonic clock handling to end users.

Stabilized APIs

The following methods and trait implementations are now stabilized:

Other changes

There are other changes in the Rust 1.60.0 release. Check out what changed inRust,Cargo, and Clippy.

Contributors to 1.60.0

Many people came together to create Rust 1.60.0. We couldn't have done it without all of you.Thanks!

Continue Reading…

Rust Blog

Security advisory for the regex crate (CVE-2022-24713)

This is a cross-post of the official security advisory. The official advisory contains a signed version with our PGP key, as well.

The Rust Security Response WG was notified that the regex crate did not properly limit the complexity of the regular expressions (regex) it parses. An attacker could use this security issue to perform a denial of service, by sending a specially crafted regex to a service accepting untrusted regexes. No known vulnerability is present when parsing untrusted input with trusted regexes.

This issue has been assigned CVE-2022-24713. The severity of this vulnerability is "high" when the regex crate is used to parse untrusted regexes. Other uses of the regex crate are not affected by this vulnerability.

Overview

The regex crate features built-in mitigations to prevent denial of service attacks caused by untrusted regexes, or untrusted input matched by trusted regexes. Those (tunable) mitigations already provide sane defaults to prevent attacks. This guarantee is documented and it's considered part of the crate's API.

Unfortunately a bug was discovered in the mitigations designed to prevent untrusted regexes to take an arbitrary amount of time during parsing, and it's possible to craft regexes that bypass such mitigations. This makes it possible to perform denial of service attacks by sending specially crafted regexes to services accepting user-controlled, untrusted regexes.

Affected versions

All versions of the regex crate before or equal to 1.5.4 are affected by this issue. The fix is include starting from regex 1.5.5.

Mitigations

We recommend everyone accepting user-controlled regexes to upgrade immediately to the latest version of the regex crate.

Unfortunately there is no fixed set of problematic regexes, as there are practically infinite regexes that could be crafted to exploit this vulnerability. Because of this, we do not recommend denying known problematic regexes.

Acknowledgements

We want to thank Addison Crump for responsibly disclosing this to us according to the Rust security policy, and for helping review the fix.

We also want to thank Andrew Gallant for developing the fix, and Pietro Albini for coordinating the disclosure and writing this advisory.

Continue Reading…

Rust Blog

Announcing Rust 1.59.0

The Rust team has published a new version of Rust, 1.59.0. Rust is a programming language that is empowering everyone to build reliable and efficient software.


Today's release falls on the day in which the world's attention is captured by the sudden invasion of Ukraine by Putin's forces. Before going into the details of the new Rust release, we'd like to state that we stand in solidarity with the people of Ukraine and express our support for all people affected by this conflict.


If you have a previous version of Rust installed via rustup, you can get 1.59.0 with:

rustup update stable

If you don't have it already, you can get rustupfrom the appropriate page on our website, and check out thedetailed release notes for 1.59.0 on GitHub.

What's in 1.59.0 stable

Inline assembly

The Rust language now supports inline assembly. This enables many applications that need very low-level control over their execution, or access to specialized machine instructions.

When compiling for x86-64 targets, for instance, you can now write:

use std::arch::asm;

// Multiply x by 6 using shifts and adds
let mut x: u64 = 4;
unsafe {
    asm!(
        &quot;mov {tmp}, {x}&quot;,
        &quot;shl {tmp}, 1&quot;,
        &quot;shl {x}, 2&quot;,
        &quot;add {x}, {tmp}&quot;,
        x = inout(reg) x,
        tmp = out(reg) _,
    );
}
assert_eq!(x, 4 * 6);

The format string syntax used to name registers in the asm! and global_asm!macros is the same used in Rust format strings, so it should feel quite familiar to Rust programmers.

The assembly language and instructions available with inline assembly vary according to the target architecture. Today, the stable Rust compiler supports inline assembly on the following architectures:

  • x86 and x86-64
  • ARM
  • AArch64
  • RISC-V

You can see more examples of inline assembly in Rust By Example, and find more detailed documentation in the reference.

Destructuring assignments

You can now use tuple, slice, and struct patterns as the left-hand side of an assignment.

let (a, b, c, d, e);

(a, b) = (1, 2);
[c, .., d, _] = [1, 2, 3, 4, 5];
Struct { e, .. } = Struct { e: 5, f: 3 };

assert_eq!([1, 2, 1, 4, 5], [a, b, c, d, e]);

This makes assignment more consistent with let bindings, which have long supported the same thing. Note that destructuring assignments with operators such as += are not allowed.

Const generics defaults and interleaving

Generic types can now specify default values for their const generics. For example, you can now write the following:

struct ArrayStorage&lt;T, const N: usize = 2&gt; {
    arr: [T; N],
}

impl&lt;T&gt; ArrayStorage&lt;T&gt; {
    fn new(a: T, b: T) -&gt; ArrayStorage&lt;T&gt; {
        ArrayStorage {
            arr: [a, b],
        }
    }
}

Previously, type parameters were required to come before all const parameters. That restriction has been relaxed and you can now interleave them.

fn cartesian_product&lt;
    T, const N: usize,
    U, const M: usize,
    V, F
&gt;(a: [T; N], b: [U; M]) -&gt; [[V; N]; M]
where
    F: FnMut(&amp;T, &amp;U) -&gt; V
{
    // ...
}

Future incompatibility warnings

Sometimes bugs in the Rust compiler cause it to accept code that should not have been accepted. An example of this was borrows of packed structfields being allowed in safe code.

While this happens very rarely, it can be quite disruptive when a crate used by your project has code that will no longer be allowed. In fact, you might not notice until your project inexplicably stops building!

Cargo now shows you warnings when a dependency will be rejected by a future version of Rust. After running cargo build or cargo check, you might see:

warning: the following packages contain code that will be rejected by a future version of Rust: old_dep v0.1.0
note: to see what the problems were, use the option `--future-incompat-report`, or run `cargo report future-incompatibilities --id 1`

You can run the cargo report command mentioned in the warning to see a full report of the code that will be rejected. This gives you time to upgrade your dependency before it breaks your build.

Creating stripped binaries

It's often useful to strip unnecessary information like debuginfo from binaries you distribute, making them smaller.

While it has always been possible to do this manually after the binary is created, cargo and rustc now support stripping when the binary is linked. To enable this, add the following to your Cargo.toml:

[profile.release]
strip = &quot;debuginfo&quot;

This causes debuginfo to be stripped from release binaries. You can also supply&quot;symbols&quot; or just true to strip all symbol information where supported.

The standard library typically ships with debug symbols and line-level debuginfo, so Rust binaries built without debug symbols enabled still include the debug information from the standard library by default. Using the stripoption allows you to remove this extra information, producing smaller Rust binaries.

See Cargo's documentation for more details.

Incremental compilation off by default

The 1.59.0 release disables incremental by default (unless explicitly asked for by via an environment variable: RUSTC_FORCE_INCREMENTAL=1). This mitigates the effects of a known bug, #94124, which can cause deserialization errors (and panics) during compilation with incremental compilation turned on.

The specific fix for #94124 has landed and is currently in the 1.60 beta, which will ship in six weeks. We are not presently aware of other issues that would encourage a decision to disable incremental in 1.60 stable, and if none arise it is likely that 1.60 stable will re-enable incremental compilation again. Incremental compilation remains on by default in the beta and nightly channels.

As always, we encourage users to test on the nightly and beta channels and report issues you find: particularly for incremental bugs, this is the best way to ensure the Rust team can judge whether there is breakage and the number of users it affects.

Stabilized APIs

The following methods and trait implementations are now stabilized:

The following previously stable functions are now const:

Other changes

There are other changes in the Rust 1.59.0 release. Check out what changed inRust,Cargo, and Clippy.

Contributors to 1.59.0

Many people came together to create Rust 1.59.0. We couldn't have done it without all of you.Thanks!

Continue Reading…

Rust Blog

rust-analyzer joins the Rust organization!

We have an exciting announcement to make! The rust-analyzer project, a new implementation of the Language Server Protocol (LSP) for Rust, is now officially a part of the wider Rust organization! 🎉

We want to start by thanking everyone who has gotten us this far, from contributors, to sponsors, to all the users of rust-analyzer in the Rust community. We could not have done this without you.

The immediate impact of this organizational change is limited -- nothing changes for rust-analyzer users or contributors. However, this change unblocks technical work to make rust-analyzer the officially recommended language server for Rust in the near future.

If you were hesitant to try rust-analyzer before, today is a good opportunity to do so. Not only is it a very capable language server for Rust, but according to VS Code statistics, it is one of the best rated LSP implementations across programming languages. We highly recommend giving rust-analyzer a spin today, even if it will take some more time for us to complete the due process and switch from the existing officially recommended LSP implementation (RLS) properly.

rust-analyzer enjoys excellent support in many editors:

For other editors, check the manual.

Finally, if you are using IntelliJ-Platform based IDEs like CLion, IDEA or PyCharm, you don't need rust-analyzer. You should use the awesome IntelliJ Rust plugin by JetBrains.

History and Future

The rust-analyzer project was started at the very end of 2017 (first commit). At that time, the existing LSP implementation, RLS, had been providing IDE support for Rust for several years. While it handled well the most important features, it was clearly far from the experience offered by state-of-the-art IDEs for some other languages.

Originally, the plan was to just experiment with error-resilient parsing for Rust; but when you have a good parser, it is so tempting to add a simple LSP server on top of it. Long story short, it took surprisingly little effort to get to a prototype which was already useful as an IDE, which happened in Autumn 2018. At that critical point, the company Ferrous Systems (which was newborn itself) stepped in to fund further development of the prototype.

During 2019, the then nascent rust-analyzer community worked hard to build out the foundation of an IDE. By 2020, we realized that what we had built was no longer a prototype, but an already tremendously useful tool for day-to-day Rust programming. This culminated in RFC2912: "Transition to rust-analyzer as our official LSP (Language Server Protocol) implementation". The RFC was accepted with overwhelming support from the community: it is still the most upvoted Rust RFC ever. However, there was a wrinkle in the plan -- rust-analyzer was not an official Rust project! That's what we are fixing today!

Next, we will proceed with the plan outlined in the RFC: advertising rust-analyzer as the very likely future of Rust IDE support, gathering feedback, and, conditioned on the positive outcome of that, sunsetting RLS, the currently recommended language server. So, once again -- do try out rust-analyzer and leave feedback on the tracking issues.

After the transition, we will double down on the technical side of things.

As exciting as rust-analyzer is today, it only scratches the surface of what's possible when you bring the compiler's intricate understanding of the code right into the text editor. The end-game we are aiming for is creating an API to analyze and transform Rust code with full access to semantics.

Funding

One of the hardest nuts to crack for the present transition was the question of funding. Today, Rust is organized as a set of somewhat independent projects (rustc, cargo, rustup, rustfmt), and there's deliberately no way to fund a specific project directly. The new Rust Foundation is the official place to sponsor Rust in general, with the Foundation Board overseeing funds allocation. Yet, it has always been encouraged for individuals to seek individual funding. While the Rust project may advertise funding opportunities for individual contributors, it does not officially endorse these efforts nor does it facilitate the funding of entire teams.

rust-analyzer has received a significant share of funds from its OpenCollective and later GitHub Sponsors, managed by Ferrous Systems. This OpenCollective funded efforts by both individual contributors and Ferrous Systems employees. Details of this can be found in their transparency reports.

Luckily, the OpenCollective has always been managed in a way that would make it possible to transfer it to a different account holder. With this transition, the OpenCollective will be renamed from "rust-analyzer OpenCollective" to "Ferrous Systems OpenCollective (rust-analyzer)". This allows current sponsors to continue to sponsor and also make it clear that their chosen project will continue to be funded.

In a sense, the OpenCollective is handed to Ferrous Systems. All Sponsor credits will move to https://ferrous-systems.com/open-source/#sponsors.

We would like to thank Ferrous Systems for their openness and flexibility in the process, for their thoughtfulness in making sure the funding situation around rust-analyzer was clear, and for taking on the effort of fundraising.

Eventually the rust-analyzer GitHub Sponsors will also move away from the rust-analyzer GitHub organisation.

And of course, another great way for companies to support rust-analyzer development is to hire the people working on rust-analyzer to continue to do so.

Thanks

We'd like to once again thank everyone who help get rust-analyzer to this point. From experiment to being well on its way to the officially recommended LSP implementation for Rust, we couldn't have done it without the help of our contributors, sponsors, and users.

Conclusion

So that's where we are at right now! Thanks to the awesome contributors to rustc, clippy, cargo, LSP, IntelliJ Rust, RLS and rust-analyzer, Rust today already enjoys great IDE support, even if it still has a bit of experimental flair to it.

Continue Reading…

Rust Blog

Rust Survey 2021 Results

Blog Post

Greetings Rustaceans!

Another year has passed, and with it comes another annual Rust survey analysis! The survey was conducted in December 2021.

We’d like to thank everyone who participated in this year’s survey, with a special shout-out to those who helped translate the survey from English into other languages.

Without further ado, let’s dive into the analysis!

A Global Community

The Rust community continues to grow, with this survey having the largest number of complete survey responses (9354 respondents), exceeding last year's total by roughly 1500 responses.

90% of respondents said that they use Rust for any purpose, while 5% stated they had used Rust at some point in the past but no longer do, and 4% stated they have yet to use Rust at all.

The survey was offered in 10 languages with 78% filling out the survey in English followed by Simplified Chinese (6%), German (4%), and French (3%). Despite English being the language most respondents completed the survey in, respondents hailed from all around the world. The United States was the country with the largest representation at 24% followed by Germany (12%), China (7%), and the U.K. (6%). In total 113 different countries and territories were represented through this survey!

English, however, is not the language of choice for all Rustaceans with nearly 7% preferring not to use English as a language for technical communication. An additional 23% of respondents prefer another language in addition to English. The most commonly preferred languages (besides English) roughly follow where Rustaceans live with Simplified Chinese, German, and French being the top 3. However, Japanese, Simplified Chinese, and Russian speakers were the most likely to prefer not to use English at all for technical conversation.

Using Rust

The percentage of people using Rust continues to rise. Of those using Rust, 81% are currently using it on at least a weekly basis compared to 72% from last year's survey.

75% of all Rust users say they can write production ready code, though 27% say that it is at times a struggle.

Rust expertise

Overall, Rustaceans seem to be having a great time writing Rust with only 1% saying it isn't fun to use. Only a quarter of a percent find Rust doesn't have any real benefit over other programming languages.

Rust at Work

Rust can now safely be classified as a language used by people in professional settings. Of those respondents using Rust, 59% use it at least occasionally at work with 23% using Rust for the majority of their coding. This is a large increase over last year where only 42% of respondents used Rust at work.

Do you use Rust at work?

Adopting Rust at work seems to follow a long but ultimately worthwhile path for a lot of Rustaceans. First, 83% of those who have adopted Rust at work found it to be "challenging". How much this is related to Rust itself versus general challenges with adopting a new programming language, however, is unclear. During adoption only 13% of respondents found the language was slowing their team down and 82% found that Rust helped their teams achieve their goals.

After adoption, the costs seem to be justified: only 1% of respondents did not find the challenge worth it while 79% said it definitely was. When asked if their teams were likely to use Rust again in the future, 90% agreed. Finally, of respondents using Rust at work, 89% of respondents said their teams found it fun and enjoyable to program.

As for why respondents are using Rust at work, the top answer was that it allowed users "to build relatively correct and bug free software" with 96% of respondents agreeing with that statement. After correctness, performance (92%) was the next most popular choice. 89% of respondents agreed that they picked Rust at work because of Rust's much-discussed security properties.

Adopting Rust

Overall, Rust seems to be a language ready for the challenges of production, with only 3% of respondents saying that Rust was a "risky" choice for production use.

Challenges Ahead

Overall, the annual survey points towards a growing, healthy community of Rustaceans, but this is not to say we don't have work ahead of us. Compile times, a historical focus of improvement for the Rust project, continue to not be where they need to be, with 61% of respondents saying work still needs to be done to improve them. Although, to the compiler team's credit, 61% found that they improved over the past year. Other areas indicated as in need of more improvement were disk space (45%), debugging (40%), and GUI development (56%).

The IDE experience (led through continued adoption and improvement of various tools like rust-analyzer, IntelliJ Rust, etc.) gets the prize for showing the most improvement: 56% found it has improved over the last year.

However, compiler error messages received the most praise, with 90% approval of their current state. 🎉

When asked what their biggest worries for the future of Rust are, the top answer was a fear that there will not be enough usage in industry (38%). Given that Rust continues to show strides in adoption at places of work, the community seems to be on a good path to overcoming this concern.

The next largest concern was that the language would become too complex (33%). This was combined with a relative small number of folks calling for additional features (especially for ones not already in the pipeline).

Finally, the third largest concern was that those working on Rust would not find the proper support they need to continue to develop the language and community in a healthy way (30%). With the establishment of the Rust Foundation, support structures are coming into place that hopefully will address this point, but no doubt plenty of work is still ahead of us.

An Exciting Future

2021 was arguably one of the most significant years in Rust's history - with the establishment of the Rust foundation, the 2021 edition, and a larger community than ever, Rust seems to be on a solid path as we head into the future.

Plenty of work remains, but here's hoping for a great 2022!

Continue Reading…

Rust Blog

Crates.io Index Snapshot Branches Moving

Every so often, the crates.io index's Git history is squashed into onecommitto minimize the history Cargo needs to download. When the index is squashed, we save snapshots to preserve the history of crate publishes.

Currently, those snapshots are stored as branches in the main index Git repository. Those branches are using server resources though, as the server still has to consider their contents whenever Cargo asks for the master branch. We will be deleting the snapshot branches from this repository to ensure that all objects referenced in the master branch will only be compressed against other objects in the master branch, ensuring that the current clone behavior will be much more efficient on the server side.

Here's how this might affect you:

If you use Cargo

You should not see any effects from this change. Cargo does not use the snapshot branches, and Cargo regularly handles index squashes. If you do see any issues, they are a bug, please reportthem on the Cargo repo.

If you use the snapshot branches

In one week, on 2022-02-21, we will be removing all snapshot branches from the crates.io-index repo. All snapshot branches, both historical and in the future, are and will be in therust-lang/crates.io-index-archive repoinstead. Please update any scripts or tools referencing the snapshot branches by that time.

In the future

In the medium term, we're working to prioritize the completion of in-progresswork to add a way to serve the index as static files on HTTP, which will further ease the server load. The index repository will not be going away so that older versions of Cargo will continue to work. See RFC2789 for more details.

Continue Reading…

Rust Blog

Changes in the Core Team

We want to say thanks to three people who recently have decided to step back from the Core Team:

  • Steve Klabnik is leaving the Core Team and the Security Response Working Group.
    Steve started with Rust more than 9 years ago.
    He was the primary author of the Rust Programming Language book and part of the core team since 2014. He has been running the @rustlang Twitter account and as such formed a lot of the “voice” of the project.
    Steve will now be able to focus on using Rust instead of making Rust.
  • Florian Gilcher is stepping back from all of his project positions.
    He became a Core Team observer in 2019 and a full Core Team member in 2020.
    He also served as a project director on the Rust foundation board.
    Before all of that he was part of the Community Team since 2015.
    As he's focusing on his company and taking on other business obligations, he is making space for others in the project.
  • Pietro Albini is leaving the Core Team to focus on other parts of the project, including the Infrastructure Team and the Security Response Working Group. He joined the Core Team in late 2019, shortly after becoming a co-lead of the Infrastructure Team, but the amount of work in the Core Team made it hard for him to spend enough time on his other roles in the project.

We're thankful for Steve's, Florian's and Pietro's contributions to the Core Team & the Rust project in the past and we’re looking forward to any contributions they will still make in the future.

Continue Reading…

Rust Blog

Announcing Rust 1.58.1

The Rust team has published a new point release of Rust, 1.58.1. Rust is a programming language that is empowering everyone to build reliable and efficient software.

If you have a previous version of Rust installed via rustup, getting Rust 1.58.1 is as easy as:

rustup update stable

If you don't have it already, you can get rustup from the appropriate page on our website.

What's in 1.58.1 stable

Rust 1.58.1 fixes a race condition in the std::fs::remove_dir_all standard library function. This security vulnerability is tracked as CVE-2022-21658, and you can read more about it on the advisory we published earliertoday. We recommend all users to update their toolchain immediately and rebuild their programs with the updated compiler.

Rust 1.58.1 also addresses several regressions in diagnostics and tooling introduced in Rust 1.58.0:

  • The non_send_fields_in_send_ty Clippy lint was discovered to have too many
    false positives and has been moved to the experimental lints group (called
    "nursery").
  • The useless_format Clippy lint has been updated to handle captured
    identifiers in format strings, introduced in Rust 1.58.0.
  • A regression in Rustfmt preventing generated files from being formatted when
    passed through the standard input has been fixed.
  • An incorrect error message displayed by rustc in some cases has been fixed.

You can find more detailed information on the specific regressions in therelease notes.

Contributors to 1.58.1

Many people came together to create Rust 1.58.1. We couldn't have done it without all of you. Thanks!

Continue Reading…

Rust Blog

Security advisory for the standard library (CVE-2022-21658)

This is a cross-post of the official security advisory. The official advisory contains a signed version with our PGP key, as well.

The Rust Security Response WG was notified that the std::fs::remove_dir_allstandard library function is vulnerable a race condition enabling symlink following (CWE-363). An attacker could use this security issue to trick a privileged program into deleting files and directories the attacker couldn't otherwise access or delete.

This issue has been assigned CVE-2022-21658.

Overview

Let's suppose an attacker obtained unprivileged access to a system and needed to delete a system directory called sensitive/, but they didn't have the permissions to do so. If std::fs::remove_dir_all followed symbolic links, they could find a privileged program that removes a directory they have access to (called temp/), create a symlink from temp/foo to sensitive/, and wait for the privileged program to delete foo/. The privileged program would follow the symlink from temp/foo to sensitive/ while recursively deleting, resulting in sensitive/ being deleted.

To prevent such attacks, std::fs::remove_dir_all already includes protection to avoid recursively deleting symlinks, as described in its documentation:

This function does not follow symbolic links and it will simply remove the symbolic link itself.

Unfortunately that check was implemented incorrectly in the standard library, resulting in a TOCTOU (Time-of-check Time-of-use) race condition. Instead of telling the system not to follow symlinks, the standard library first checked whether the thing it was about to delete was a symlink, and otherwise it would proceed to recursively delete the directory.

This exposed a race condition: an attacker could create a directory and replace it with a symlink between the check and the actual deletion. While this attack likely won't work the first time it's attempted, in our experimentation we were able to reliably perform it within a couple of seconds.

Affected Versions

Rust 1.0.0 through Rust 1.58.0 is affected by this vulnerability. We're going to release Rust 1.58.1 later today, which will include mitigations for this vulnerability. Patches to the Rust standard library are also available for custom-built Rust toolchains here.

Note that the following targets don't have usable APIs to properly mitigate the attack, and are thus still vulnerable even with a patched toolchain:

  • macOS before version 10.10 (Yosemite)
  • REDOX

Mitigations

We recommend everyone to update to Rust 1.58.1 as soon as possible, especially people developing programs expected to run in privileged contexts (including system daemons and setuid binaries), as those have the highest risk of being affected by this.

Note that adding checks in your codebase before calling remove_dir_all willnot mitigate the vulnerability, as they would also be vulnerable to race conditions like remove_dir_all itself. The existing mitigation is working as intended outside of race conditions.

Acknowledgments

We want to thank Hans Kratz for independently discovering and disclosing this issue to us according to the Rust security policy, for developing the fix for UNIX-like targets and for reviewing fixes for other platforms.

We also want to thank Florian Weimer for reviewing the UNIX-like fix and for reporting the same issue back in 2018, even though the Security Response WG didn't realize the severity of the issue at the time.

Finally we want to thank Pietro Albini for coordinating the security response and writing this advisory, Chris Denton for writing the Windows fix, Alex Crichton for writing the WASI fix, and Mara Bos for reviewing the patches.

Continue Reading…

Rust Blog

Announcing Rust 1.58.0

The Rust team is happy to announce a new version of Rust, 1.58.0. Rust is a programming language empowering everyone to build reliable and efficient software.

If you have a previous version of Rust installed via rustup, getting Rust 1.58.0 is as easy as:

rustup update stable

If you don't have it already, you can get rustupfrom the appropriate page on our website, and check out thedetailed release notes for 1.58.0 on GitHub.

What's in 1.58.0 stable

Rust 1.58 brings captured identifiers in format strings, a change to theCommand search path on Windows, more #[must_use] annotations in the standard library, and some new library stabilizations.

Captured identifiers in format strings

Format strings can now capture arguments simply by writing {ident} in the string. Formats have long accepted positional arguments (optionally by index) and named arguments, for example:

println!(&quot;Hello, {}!&quot;, get_person());                // implicit position
println!(&quot;Hello, {0}!&quot;, get_person());               // explicit index
println!(&quot;Hello, {person}!&quot;, person = get_person()); // named

Now named arguments can also be captured from the surrounding scope, like:

let person = get_person();
// ...
println!(&quot;Hello, {person}!&quot;); // captures the local `person`

This may also be used in formatting parameters:

let (width, precision) = get_format();
for (name, score) in get_scores() {
  println!(&quot;{name}: {score:width$.precision$}&quot;);
}

Format strings can only capture plain identifiers, not arbitrary paths or expressions. For more complicated arguments, either assign them to a local name first, or use the older name = expression style of formatting arguments.

This feature works in all macros accepting format strings. However, one corner case is the panic! macro in 2015 and 2018 editions, where panic!(&quot;{ident}&quot;)is still treated as an unformatted string -- the compiler will warn about this not having the intended effect. Due to the 2021 edition's update of panic macros for improved consistency, this works as expected in 2021 panic!.

Reduced Windows Command search path

On Windows targets, std::process::Command will no longer search the current directory for executables. That effect was owed to historical behavior of the win32 CreateProcess API, so Rust was effectively searching in this order:

  1. (Rust specific) The directories that are listed in the child's PATHenvironment variable, if it was explicitly changed from the parent.
  2. The directory from which the application loaded.
  3. The current directory for the parent process.
  4. The 32-bit Windows system directory.
  5. The 16-bit Windows system directory.
  6. The Windows directory.
  7. The directories that are listed in the PATH environment variable.

However, using the current directory can lead to surprising results, or even malicious behavior when dealing with untrusted directories. For example,ripgrep published CVE-2021-3013 when they learned that their child processes could be intercepted in this way. Even Microsoft's own PowerShelldocuments that they do not use the current directory for security.

Rust now performs its own search without the current directory, and the legacy 16-bit directory is also not included, as there is no API to discover its location. So the new Command search order for Rust on Windows is:

  1. The directories that are listed in the child's PATH environment variable.
  2. The directory from which the application loaded.
  3. The 32-bit Windows system directory.
  4. The Windows directory.
  5. The directories that are listed in the PATH environment variable.

Non-Windows targets continue to use their platform-specific behavior, most often only considering the child or parent PATH environment variable.

More #[must_use] in the standard library

The #[must_use] attribute can be applied to types or functions when failing to explicitly consider them or their output is almost certainly a bug. This has long been used in the standard library for types like Result, which should be checked for error conditions. This also helps catch mistakes such as expecting a function to mutate a value in-place, when it actually returns a new value.

Library proposal 35 was approved in October 2021 to audit and expand the application of #[must_use] throughout the standard library, covering many more functions where the primary effect is the return value. This is similar to the idea of function purity, but looser than a true language feature. Some of these additions were present in release 1.57.0, and now in 1.58.0 the effort has completed.

Stabilized APIs

The following methods and trait implementations were stabilized.

The following previously stable functions are now const.

Other changes

There are other changes in the Rust 1.58.0 release: check out what changed inRust,Cargo, and Clippy.

Contributors to 1.58.0

Many people came together to create Rust 1.58.0. We couldn't have done it without all of you.Thanks!

Continue Reading…