Rust Blog: Posts

Rust Blog

Google Summer of Code 2024 results

As we have previously announced, the Rust Project participated in Google Summer of Code (GSoC) for the first time this year. Nine contributors have been tirelessly working on their exciting projects for several months. The projects had various durations; some of them have ended in August, while the last one has been concluded in the middle of October. Now that the final reports of all the projects have been submitted, we can happily announce that all nine contributors have passed the final review! That means that we have deemed all of their projects to be successful, even though they might not have fulfilled all of their original goals (but that was expected).

We had a lot of great interactions with our GSoC contributors, and based on their feedback, it seems that they were also quite happy with the GSoC program and that they had learned a lot. We are of course also incredibly grateful for all their contributions - some of them have even continued contributing after their project has ended, which is really awesome. In general, we think that Google Summer of Code 2024 was a success for the Rust Project, and we are looking forward to participating in GSoC (or similar programs) again in the near future. If you are interested in becoming a (GSoC) contributor, check out our project idea list.

Below you can find a brief summary of each of our GSoC 2024 projects, including feedback from the contributors and mentors themselves. You can find more information about the projectshere.

Adding lint-level configuration to cargo-semver-checks

cargo-semver-checks is a tool designed for automatically detecting semantic versioning conflicts, which is planned to one day become a part of Cargo itself. The goal of this project was to enable cargo-semver-checks to ship additional opt-in lints by allowing users to configure which lints run in which cases, and whether their findings are reported as errors or warnings. Max achieved this goal by implementing a comprehensive system for configuring cargo-semver-checks lints directly in the Cargo.toml manifest file. He also extensively discussed the design with the Cargo team to ensure that it is compatible with how other Cargo lints are configured, and won't present a future compatibility problem for merging cargo-semver-checks into Cargo.

Predrag, who is the author of cargo-semver-checks and who mentored Max on this project, was very happy with his contributions that even went beyond his original project scope:

He designed and built one of our most-requested features, and produced design prototypes of several more features our users would love. He also observed that writing quality CLI and functional tests was hard, so he overhauled our test system to make better tests easier to make. Future work on cargo-semver-checks will be much easier thanks to the work Max put in this summer.

Great work, Max!

Implementation of a faster register allocator for Cranelift

The Rust compiler can use various backends for generating executable code. The main one is of course the LLVM backend, but there are other backends, such as GCC, .NET or Cranelift. Cranelift is a code generator for various hardware targets, essentially something similar to LLVM. The Cranelift backend uses Cranelift to compile Rust code into executable code, with the goal of improving compilation performance, especially for debug (unoptimized) builds. Even though this backend can already be faster than the LLVM backend, we have identified that it was slowed down by the register allocator used by Cranelift.

Register allocation is a well-known compiler task where the compiler decides which registers should hold variables and temporary expressions of a program. Usually, the goal of register allocation is to perform the register assignment in a way that maximizes the runtime performance of the compiled program. However, for unoptimized builds, we often care more about the compilation speed instead.

Demilade has thus proposed to implement a new Cranelift register allocator called fastalloc, with the goal of making it as fast as possible, at the cost of the quality of the generated code. He was very well-prepared, in fact he had a prototype implementation ready even before his GSoC project has started! However, register allocation is a complex problem, and thus it then took several months to finish the implementation and also optimize it as much as possible. Demilade also made extensive use of fuzzing to make sure that his allocator is robust even in the presence of various edge cases.

Once the allocator was ready, Demilade benchmarked the Cranelift backend both with the original and his new register allocator using our compiler benchmark suite. And the performance results look awesome! With his faster register allocator, the Rust compiler executes up to 18% less instructions across several benchmarks, including complex ones like performing a debug build of Cargo itself. Note that this is an end-to-end performance improvement of the time needed to compile a whole crate, which is really impressive. If you would like to examine the results in more detail or even run the benchmark yourself, check out Demilade's final report, which includes detailed instructions on how to reproduce the benchmark.

Apart from having the potential to speed up compilation of Rust code, the new register allocator can be also useful for other use-cases, as it can be used in Cranelift on its own (outside the Cranelift codegen backend). What can we can say other than we are very happy with Demilade's work! Note that the new register allocator is not yet available in the Cranelift codegen backend out-of-the-box, but we expect that it will eventually become the default choice for debug builds and that it will thus make compilation of Rust crates using the Cranelift backend faster in the future.

Improve Rust benchmark suite

This project was relatively loosely defined, with the overarching goal of improving the user interface of the Rust compiler benchmark suite. Eitaro tackled this challenge from various angles at once. He improved the visualization of runtime benchmarks, which were previously a second-class citizen in the benchmark suite, by adding them to our dashboard and by implementing historical charts of runtime benchmark results, which help us figure out how is a given benchmark behaving over a longer time span.

Another improvement that he has worked on was embedding a profiler trace visualizer directly within the rustc-perf website. This was a challenging task, which required him to evaluate several visualizers and figure out a way how to include them within the source code of the benchmark suite in a non-disruptive way. In the end, he managed to integrate Perfetto within the suite website, and also performed various optimizations to improve the performance of loading compilation profiles.

Last, but not least, Eitaro also created a completely new user interface for the benchmark suite, which runs entirely in the terminal. Using this interface, Rust compiler contributors can examine the performance of the compiler without having to start the rustc-perf website, which can be challenging to deploy locally.

Apart from the mentioned contributions, Eitaro also made a lot of other smaller improvements to various parts of the benchmark suite. Thank you for all your work!

Move cargo shell completions to Rust

Cargo's completion scripts have been hand maintained and frequently broken when changed. The goal for this effort was to have the completions automatically generated from the definition of Cargo's command-line, with extension points for dynamically generated results.

shanmu took the prototype for dynamic completions in clap (the command-line parser used by Cargo), got it working and tested for common shells, as well as extended the parser to cover more cases. They then added extension points for CLI's to provide custom completion results that can be generated on the fly.

In the next phase, shanmu added this to nightly Cargo and added different custom completers to match what the handwritten completions do. As an example, with this feature enabled, when you type cargo test --test= and hit the Tab key, your shell will autocomplete all the test targets in your current Rust crate! If you are interested, see the instructions for trying this out. The link also lists where you can provide feedback.

You can also check out the following issues to find out what is left before this can be stabilized:

Rewriting esoteric, error-prone makefile tests using robust Rust features

The Rust compiler has several test suites that make sure that it is working correctly under various conditions. One of these suites is the run-make test suite, whose tests were previously written using Makefiles. However, this setup posed several problems. It was not possible to run the suite on the Tier 1 Windows MSVC target (x86_64-pc-windows-msvc) and getting it running on Windows at all was quite challenging. Furthermore, the syntax of Makefiles is quite esoteric, which frequently caused mistakes to go unnoticed even when reviewed by multiple people.

Julien helped to convert the Makefile-based run-make tests into plain Rust-based tests, supported by a test support library called run_make_support. However, it was not a trivial "rewrite this in Rust" kind of deal. In this project, Julien:

  • Significantly improved the test documentation;
  • Fixed multiple bugs that were present in the Makefile versions that had gone unnoticed for years -- some tests were never testing anything or silently ignored failures, so even if the subject being tested regressed, these tests would not have caught that.
  • Added to and improved the test support library API and implementation; and
  • Improved code organization within the tests to make them easier to understand and maintain.

Just to give you an idea of the scope of his work, he has ported almost 250 Makefile tests over the span of his GSoC project! If you like puns, check out the branch names of Julien's PRs, as they are simply fantestic.

As a result, Julien has significantly improved the robustness of the run-make test suite, and improved the ergonomics of modifying existing run-make tests and authoring new run-make tests. Multiple contributors have expressed that they were more willing to work with the Rust-based run-make tests over the previous Makefile versions.

The vast majority of run-make tests now use the Rust-based test infrastructure, with a few holdouts remaining due to various quirks. After these are resolved, we can finally rip out the legacy Makefile test infrastructure.

Rewriting the Rewrite trait

rustfmt is a Rust code formatter that is widely used across the Rust ecosystem thanks to its direct integration within Cargo. Usually, you just run cargo fmt and you can immediately enjoy a properly formatted Rust project. However, there are edge cases in which rustfmt can fail to format your code. That is not such an issue on its own, but it becomes more problematic when it fails silently, without giving the user any context about what went wrong. This is what was happening in rustfmt, as many functions simply returned an Option instead of a Result, which made it difficult to add proper error reporting.

The goal of SeoYoung's project was to perform a large internal refactoring of rustfmt that would allow tracking context about what went wrong during reformatting. In turn, this would enable turning silent failures into proper error messages that could help users examine and debug what went wrong, and could even allow rustfmt to retry formatting in more situations.

At first, this might sound like an easy task, but performing such large-scale refactoring within a complex project such as rustfmt is not so simple. SeoYoung needed to come up with an approach to incrementally apply these refactors, so that they would be easy to review and wouldn't impact the entire code base at once. She introduced a new trait that enhanced the original Rewrite trait, and modified existing implementations to align with it. She also had to deal with various edge cases that we hadn't anticipated before the project started. SeoYoung was meticulous and systematic with her approach, and made sure that no formatting functions or methods were missed.

Ultimately, the refactor was a success! Internally, rustfmt now keeps track of more information related to formatting failures, including errors that it could not possibly report before, such as issues with macro formatting. It also has the ability to provide information about source code spans, which helps identify parts of code that require spacing adjustments when exceeding the maximum line width. We don't yet propagate that additional failure context as user facing error messages, as that was a stretch goal that we didn't have time to complete, but SeoYoung has expressed interest in continuing to work on that as a future improvement!

Apart from working on error context propagation, SeoYoung also made various other improvements that enhanced the overall quality of the codebase, and she was also helping other contributors understand rustfmt. Thank you for making the foundations of formatting better for everyone!

Rust to .NET compiler - add support for compiling & running cargo tests

As was already mentioned above, the Rust compiler can be used with various codegen backends. One of these is the .NET backend, which compiles Rust code to the Common Intermediate Language (CIL), which can then be executed by the .NET Common Language Runtime (CLR). This backend allows interoperability of Rust and .NET (e.g. C#) code, in an effort to bring these two ecosystems closer together.

At the start of this year, the .NET backend was already able to compile complex Rust programs, but it was still lacking certain crucial features. The goal of this GSoC project, implemented by Michał, who is in fact the sole author of the backend, was to extend the functionality of this backend in various areas. As a target goal, he set out to extend the backend so that it could be used to run tests using the cargo test command. Even though it might sound trivial, properly compiling and running the Rust test harness is non-trivial, as it makes use of complex features such as dynamic trait objects, atomics, panics, unwinding or multithreading. These features were especially tricky to implement in this codegen backend, because the LLVM intermediate representation (IR) and CIL have fundamental differences, and not all LLVM intrinsics have .NET equivalents.

However, this did not stop Michał. He has been working on this project tirelessly, implementing new features, fixing various issues and learning more about the compiler's internals every new day. He has also been documenting his journey with (almost) daily updates on Zulip, which were fascinating to read. Once he has reached his original goal, he moved the goalpost up to another level and attempted to run the compiler's own test suite using the .NET backend. This helped him uncover additional edge cases and also led to a refactoring of the whole backend that resulted in significant performance improvements.

By the end of the GSoC project, the .NET backend was able to properly compile and run almost 90% of the standard library core and std test suite. That is an incredibly impressive number, since the suite contains thousands of tests, some of which are quite arcane. Michał's pace has not slowed down even after the project has ended and he is still continuously improving the backend. Oh, and did we already mention that his backend also has experimental support for emitting C code, effectively acting as a C codegen backend?! Michał has been very busy over the summer.

We thank Michał for all his work on the .NET backend, as it was truly inspirational, and led to fruitful discussions that were relevant also to other codegen backends. Michał's next goal is to get his backend upstreamed and create an official .NET compilation target, which could open up the doors to Rust becoming a first-class citizen in the .NET ecosystem.

Sandboxed and deterministic proc macro using WebAssembly

Rust procedural (proc) macros are currently run as native code that gets compiled to a shared object which is loaded directly into the process of the Rust compiler. Because of this design, these macros can do whatever they want, for example arbitrarily access the filesystem or communicate through a network. This has not only obvious security implications, but it also affects performance, as this design makes it difficult to cache proc macro invocations. Over the years, there have been various discussions about making proc macros more hermetic, for example by compiling them to WebAssembly modules, which can be easily executed in a sandbox. This would also open the possibility of distributing precompiled versions of proc macros via crates.io, to speed up fresh builds of crates that depend on proc macros.

The goal of this project was to examine what would it take to implement WebAssembly module support for proc macros and create a prototype of this idea. We knew this would be a very ambitious project, especially since Apurva did not have prior experience with contributing to the Rust compiler, and because proc macro internals are very complex. Nevertheless, some progress was made. With the help of his mentor, David, Apurva was able to create a prototype that can load WebAssembly code into the compiler via a shared object. Some work was also done to make use of the existing TokenStream serialization and deserialization code in the compiler's proc_macro crate.

Even though this project did not fulfill its original goals and more work will be needed in the future to get a functional prototype of WebAssembly proc macros, we are thankful for Apurva's contributions. The WebAssembly loading prototype is a good start, and Apurva's exploration of proc macro internals should serve as a useful reference for anyone working on this feature in the future. Going forward, we will try to describe more incremental steps for our GSoC projects, as this project was perhaps too ambitious from the start.

Tokio async support in Miri

miri is an intepreter that can find possible instances of undefined behavior in Rust code. It is being used across the Rust ecosystem, but previously it was not possible to run it on any non-trivial programs (those that ever await on anything) that use tokio, due a to a fundamental missing feature: support for the epoll syscall on Linux (and similar APIs on other major platforms).

Tiffany implemented the basic epoll operations needed to cover the majority of the tokio test suite, by crafting pure libc code examples that exercised those epoll operations, and then implementing their emulation in miri itself. At times, this required refactoring core miri components like file descriptor handling, as they were originally not created with syscalls like epoll in mind.

Suprising to everyone (though probably not tokio-internals experts), once these core epoll operations were finished, operations like async file reading and writing started working in miri out of the box! Due to limitations of non-blocking file operations offered by operating systems, tokio is wrapping these file operations in dedicated threads, which was already supported by miri.

Once Tiffany has finished the project, including stretch goals like implementing async file operations, she proceeded to contact tokio maintainers and worked with them to run miri on most tokio tests in CI. And we have good news: so far no soundness problems have been discovered! Tiffany has become a regular contributor to miri, focusing on continuing to expand the set of supported file descriptor operations. We thank her for all her contributions!

Conclusion

We are grateful that we could have been a part of the Google Summer of Code 2024 program, and we would also like to extend our gratitude to all our contributors! We are looking forward to joining the GSoC program again next year.

Continue Reading…

Rust Blog

gccrs: An alternative compiler for Rust

gccrs is a work-in-progress alternative compiler for Rust being developed as part of the GCC project. GCC is a collection of compilers for various programming languages that all share a common compilation framework. You may have heard about gccgo, gfortran, or g++, which are all binaries within that project, the GNU Compiler Collection. The aim of gccrs is to add support for the Rust programming language to that collection, with the goal of having the exact same behavior as rustc.

First and foremost, gccrs was started as a project because it is fun. Compilers are incredibly rewarding pieces of software, and are great fun to put together. The project was started back in 2014, before Rust 1.0 was released, but was quickly put aside due to the shifting nature of the language back then. Around 2019, work on the compiler started again, led by Philip Herron and funded by Open Source Security and Embecosm. Since then, we have kept steadily progressing towards support for the Rust language as a whole, and our team has kept growing with around a dozen contributors working regularly on the project. We have participated in the Google Summer of Code program for the past four years, and multiple students have joined the effort.

The main goal of gccrs is to provide an alternative option for compiling Rust. GCC is an old project, as it was first released in 1987. Over the years, it has accumulated numerous contributions and support for multiple targets, including some not supported by LLVM, the main backend used by rustc. A practical example of that reach is the homebrew Dreamcast scene, where passionate engineers develop games for the Dreamcast console. Its processor architecture, SuperH, is supported by GCC but not by LLVM. This means that Rust is not able to be used on those platforms, except through efforts like gccrs or the rustc-codegen-gcc backend - whose main differences will be explained later.

GCC also benefits from the decades of software written in unsafe languages. As such, a high amount of safety features have been developed for the project as external plugins, or even within the project as static analyzers. These analyzers and plugins are executed on GCC's internal representations, meaning that they are language-agnostic, and can thus be used on all the programming languages supported by GCC. Likewise, many GCC plugins are used for increasing the safety of critical projects such as the Linux kernel, which has recently gained support for the Rust programming language. This makes gccrs a useful tool for analyzing unsafe Rust code, and more generally Rust code which has to interact with existing C code. We also want gccrs to be a useful tool for rustc itself by helping pan out the Rust specification effort with a unique viewpoint - that of a tool trying to replicate another's functionality, oftentimes through careful experimentation and source reading where the existing documentation did not go into enough detail. We are also in the process of developing various tools around gccrs and rustc, for the sole purpose of ensuring gccrs is as correct as rustc - which could help in discovering surprising behavior, unexpected functionality, or unspoken assumptions.

We would like to point out that our goal in aiding the Rust specification effort is not to turn it into a document for certifying alternative compilers as "Rust compilers" - while we believe that the specification will be useful to gccrs, our main goal is to contribute to it, by reviewing and adding to it as much as possible.

Furthermore, the project is still "young", and still requires a huge amount of work. There are a lot of places to make your mark, and a lot of easy things to work on for contributors interested in compilers. We have strived to create a safe, fun, and interesting space for all of our team and our GSoC students. We encourage anyone interested to come chat with us on our various communication platforms, and offer mentorship for you to learn how to contribute to the project and to compilers in general.

Maybe more importantly however, there is a number of things that gccrs is NOT for. The project has multiple explicit non-goals, which we value just as highly as our goals.

The most crucial of these non-goals is for gccrs not to become a gateway for an alternative or extended Rust-like programming language. We do not wish to create a GNU-specific version of Rust, with different semantics or slightly different functionality. gccrs is not a way to introduce new Rust features, and will not be used to circumvent the RFC process - which we will be using, should we want to see something introduced to Rust. Rust is not C, and we do not intend to introduce subtle differences in standard by making some features available only to gccrs users. We know about the pain caused by compiler-specific standards, and have learned from the history of older programming languages.

We do not want gccrs to be a competitor to the rustc_codegen_gcc backend. While both projects will effectively achieve the same goal, which is to compile Rust code using the GCC compiler framework, there are subtle differences in what each of these projects will unlock for the language. For example, rustc_codegen_gcc makes it easy to benefit from all of rustc's amazing diagnostics and helpful error messages, and makes Rust easily usable on GCC-specific platforms. On the other hand, it requires rustc to be available in the first place, whereas gccrs is part of a separate project entirely. This is important for some users and core Linux developers for example, who believe that having the ability to compile the entire kernel (C and Rust parts) using a single compiler is essential. gccrs can also offer more plugin entrypoints by virtue of it being its own separate GCC frontend. It also allows Rust to be used on GCC-specific platforms with an older GCC where libgccjit is not available. Nonetheless, we are very good friends with the folks working on rustc_codegen_gcc, and have helped each other multiple times, especially in dealing with the patch-based contribution process that GCC uses.

All of this ties into a much more global goal, which we could summarize as the following: We do not want to split the Rust ecosystem. We want gccrs to help the language reach even more people, and even more platforms.

To ensure that, we have taken multiple measures to make sure the values of the Rust project are respected and exposed properly. One of the features we feel most strongly about is the addition of a very annoying command line flag to the compiler, -frust-incomplete-and-experimental-compiler-do-not-use. Without it, you are not able to compile any code with gccrs, and the compiler will output the following error message:

crab1: fatal error: gccrs is not yet able to compile Rust code properly. Most of the errors produced will be the fault of gccrs and not the crate you are trying to compile. Because of this, please report errors directly to us instead of opening issues on said crate's repository.

Our github repository: https://github.com/rust-gcc/gccrs

Our bugzilla tracker: https://gcc.gnu.org/bugzilla/buglist.cgi?bug_status=__open__&component=rust&product=gcc

If you understand this, and understand that the binaries produced might not behave accordingly, you may attempt to use gccrs in an experimental manner by passing the following flag:

-frust-incomplete-and-experimental-compiler-do-not-use

or by defining the following environment variable (any value will do)

GCCRS_INCOMPLETE_AND_EXPERIMENTAL_COMPILER_DO_NOT_USE

For cargo-gccrs, this means passing

GCCRS_EXTRA_ARGS="-frust-incomplete-and-experimental-compiler-do-not-use"

as an environment variable.

Until the compiler can compile correct Rust and, most importantly, reject incorrect Rust, we will be keeping this command line option in the compiler. The hope is that it will prevent users from potentially annoying existing Rust crate maintainers with issues about code not compiling, when it is most likely our fault for not having implemented part of the language yet. Our goal of creating an alternative compiler for the Rust language must not have a negative effect on any member of the Rust community. Of course, this command line flag is not to the taste of everyone, and there has been significant pushback to its presence... but we believe it to be a good representation of our main values.

In a similar vein, gccrs separates itself from the rest of the GCC project by not using a mailing list as its main mode of communication. The compiler we are building will be used by the Rust community, and we believe we should make it easy for that community to get in touch with us and report the problems they encounter. Since Rustaceans are used to GitHub, this is also the development platform we have been using for the past five years. Similarly, we use a Zulip instance as our main communication platform, and encourage anyone wanting to chat with us to join it. Note that we still have a mailing list, as well as an IRC channel (gcc-rust@gcc.gnu.org and #gccrust on oftc.net), where all are welcome.

To further ensure that gccrs does not create friction in the ecosystem, we want to be extremely careful about the finer details of the compiler, which to us means reusing rustc components where possible, sharing effort on those components, and communicating extensively with Rust experts in the community. Two Rust components are already in use by gccrs: a slightly older version of polonius, the next-generation Rust borrow-checker, and the rustc_parse_format crate of the compiler. There are multiple reasons for reusing these crates, with the main one being correctness. Borrow checking is a complex topic and a pillar of the Rust programming language. Having subtle differences between rustc and gccrs regarding the borrow rules would be annoying and unproductive to users - but by making an effort to start integrating polonius into our compilation pipeline, we help ensure that the results we produce will be equivalent to rustc. You can read more about the various components we use, and we plan to reuse even more here. We would also like to contribute to the polonius project itself and help make it better if possible. This cross-pollination of components will obviously benefit us, but we believe it will also be useful for the Rust project and ecosystem as a whole, and will help strengthen these implementations.

Reusing rustc components could also be extended to other areas of the compiler: Various components of the type system, such as the trait solver, an essential and complex piece of software, could be integrated into gccrs. Simpler things such as parsing, as we have done for the format string parser and inline assembly parser, also make sense to us. They will help ensure that the internal representation we deal with will correspond to the one expected by the Rust standard library.

On a final note, we believe that one of the most important steps we could take to prevent breakage within the Rust ecosystem is to further improve our relationship with the Rust community. The amount of help we have received from Rust folks is great, and we think gccrs can be an interesting project for a wide range of users. We would love to hear about your hopes for the project and your ideas for reducing ecosystem breakage or lowering friction with the crates you have published. We had a great time chatting about gccrs at RustConf 2024, and everyone's interest in the project was heartwarming. Please get in touch with us if you have any ideas on how we could further contribute to Rust.

Continue Reading…

Rust Blog

Next Steps on the Rust Trademark Policy

As many of you know, the Rust language trademark policy has been the subject of an extended revision process dating back to 2022. In 2023, the Rust Foundation released an updated draft of the policy for input following an initial survey about community trademark priorities from the previous year along with review by other key stakeholders, such as the Project Directors. Many members of our community were concerned about this initial draft and shared their thoughts through the feedback form. Since then, the Rust Foundation has continued to engage with the Project Directors, the Leadership Council, and the wider Rust project (primarily via all@) for guidance on how to best incorporate as much feedback as possible.

After extensive discussion, we are happy to circulate an updated draft with the wider community today for final feedback. An effective trademark policy for an open source community should reflect our collective priorities while remaining legally sound. While the revised trademark policy cannot perfectly address every individual perspective on this important topic, its goal is to establish a framework to help guide appropriate use of the Rust trademark and reflect as many common values and interests as possible. In short, this policy is designed to steer our community toward a shared objective: to maintain and protect the integrity of the Rust programming language.

The Leadership Council is confident that this updated version of the policy has addressed the prevailing concerns about the initial draft and honors the variety of voices that have contributed to its development. Thank you to those who took the time to submit well-considered feedback for the initial draft last year or who otherwise participated in this long-running process to update our policy to continue to satisfy our goals.

Please review the updated Rust trademark policy here, and share any critical concerns you might have via this form by November 20, 2024. The Foundation has also published a blog post which goes into more detail on the changes made so far. The Leadership Council and Project Directors look forward to reviewing concerns raised and approving any final revisions prior to an official update of the policy later this year.

Continue Reading…

Rust Blog

October project goals update

The Rust project is currently working towards a slate of 26 project goals, with 3 of them designed as flagship goals. This post provides selected updates on our progress towards these goals (or, in some cases, lack thereof). The full details for any particular goal are available in its associated tracking issue on the rust-project-goals repository.

Flagship goals

Bring the async Rust experience closer to parity with sync Rust

The biggest elements of our goal are solving the "send bound" problem via return-type notation (RTN) and adding support for async closures. This month we made progress towards both. For RTN, @compiler-errors extended the return-type notation landed support for using RTN in self-types like where Self::method(): Send. He also authored a blog post with a call for testing explaining what RTN is and how it works. For async closures, the lang team reached a preliminary consensus on the async Fn syntax, with the understanding that it will also include some "async type" syntax. This rationale was documented in RFC #3710, which is now open for feedback. The team held a design meeting on Oct 23 and @nikomatsakis will be updating the RFC with the conclusions.

We have also been working towards a release of the dynosaur crate that enables dynamic dispatch for traits with async functions. This is intended as a transitionary step before we implement true dynamic dispatch. The next steps are to polish the implementation and issue a public call for testing.

With respect to async drop experiments, @nikomatsakis began reviews. It is expected that reviews will continue for some time as this is a large PR.

Finally, no progress has been made towards async WG reorganization. A meeting was scheduled but deferred. @tmandry is currently drafting an initial proposal.

Resolve the biggest blockers to Linux building on stable Rust

We have made significant progress on resolving blockers to Linux building on stable. Support for struct fields in the offset_of! macro has been stabilized. The final naming for the "derive-smart-pointer" feature has been decided as #[derive(CoercePointee)]; @dingxiangfei2009 prepared PR #131284 for the rename and is working on modifying the rust-for-linux repository to use the new name. Once that is complete, we will be able to stabilize. We decided to stabilize support for references to statics in constants pointers-refs-to-static feature and are now awaiting a stabilization PR from @dingxiangfei2009.

Rust for Linux (RfL) is one of the major users of the asm-goto feature (and inline assembly in general) and we have been examining various extensions. @nbdd0121 authored a hackmd document detailing RfL's experiences and identifying areas for improvement. This led to two immediate action items: making target blocks safe-by-default (rust-lang/rust#119364) and extending const to support embedded pointers (rust-lang/rust#128464).

Finally, we have been finding an increasing number of stabilization requests at the compiler level, and so @wesleywiser and @davidtwco from the compiler team have started attending meetings to create a faster response. One of the results of that collaboration is RFC #3716, authored by Alice Ryhl, which proposes a method to manage compiler flags that modify the target ABI. Our previous approach has been to create distinct targets for each combination of flags, but the number of flags needed by the kernel make that impractical. Authoring the RFC revealed more such flags than previously recognized, including those that modify LLVM behavior.

Rust 2024 edition

The Rust 2024 edition is progressing well and is on track to be released on schedule. The major milestones include preparing to stabilize the edition by November 22, 2024, with the actual stabilization occurring on November 28, 2024. The edition will then be cut to beta on January 3, 2025, followed by an announcement on January 9, 2025, indicating that Rust 2024 is pending release. The final release is scheduled for February 20, 2025.

The priorities for this edition have been to ensure its success without requiring excessive effort from any individual. The team is pleased with the progress, noting that this edition will be the largest since Rust 2015, introducing many new and exciting features. The process has been carefully managed to maintain high standards without the need for high-stress heroics that were common in past editions. Notably, the team has managed to avoid cutting many items from the edition late in the development process, which helps prevent wasted work and burnout.

All priority language items for Rust 2024 have been completed and are ready for release. These include several key issues and enhancements. Additionally, there are three changes to the standard library, several updates to Cargo, and an exciting improvement to rustdoc that will significantly speed up doctests.

This edition also introduces a new style edition for rustfmt, which includes several formatting changes.

The team is preparing to start final quality assurance crater runs. Once these are triaged, the nightly beta for Rust 2024 will be announced, and wider testing will be solicited.

Rust 2024 will be stabilized in nightly in late November 2024, cut to beta on January 3, 2025, and officially released on February 20, 2025. More details about the edition items can be found in the Edition Guide.

Goals with updates

"Stabilizable" prototype for expanded const generics

  • camelid has started working on using the new lowering schema for more than just const parameters, which once done will allow the introduction of a min_generic_const_args feature gate.
  • compiler-errors has been working on removing the eval_x methods on Const that do not perform proper normalization and are incompatible with this feature.

Assemble project goal slate

  • Posted the September update.
  • Created more automated infrastructure to prepare the October update, utilizing an LLM to summarize updates into one or two sentences for a concise table.

Associated type position impl trait

Will not complete

  • No progress has been made on this goal.
  • The goal will be closed as consensus indicates stabilization will not be achieved in this period; it will be revisited in the next goal period.

Begin resolving `cargo-semver-checks` blockers for merging into cargo

  • No major updates to report.
  • Preparing a talk for next week's EuroRust has taken away most of the free time.

Const traits

  • Key developments: With the PR for supporting implied super trait bounds landed (#129499), the current implementation is mostly complete in that it allows most code that should compile, and should reject all code that shouldn't.
  • Further testing is required, with the next steps being improving diagnostics (#131152), and fixing more holes before const traits are added back to core.

Explore sandboxed build scripts

  • A working-in-process pull request is available at https://github.com/weihanglo/cargo/pull/66.
  • The use of wasm32-wasip1 as a default sandbox environment is unlikely due to its lack of support for POSIX process spawning, which is essential for various build script use cases.

Expose experimental LLVM features for automatic differentiation and GPU offloading

  • The Autodiff frontend was merged, including over 2k LoC and 30 files, making the remaining diff much smaller.
  • The Autodiff middle-end is likely getting a redesign, moving from a library-based to a pass-based approach for LLVM.

Extend pubgrub to match cargo's dependency resolution

  • Significant progress was made with contributions by @x-hgg-x, improving the resolver test suite in Cargo to check feature unification against a SAT solver.
  • This was followed by porting the test cases that tripped up PubGrub to Cargo's test suite, laying the groundwork to prevent regression on important behaviors when Cargo switches to PubGrub and preparing for fuzzing of features in dependency resolution.

Make Rustdoc Search easier to learn

  • The team is working on a consensus for handling generic parameters, with both PRs currently blocked on this issue.

Next-generation trait solver

  • Attempted stabilization of -Znext-solver=coherence was reverted due to a hang in nalgebra, with subsequent fixes improving but not fully resolving performance issues.
  • No significant changes to the new solver have been made in the last month.

Optimizing Clippy & linting

  • GnomedDev pushed rust-lang/rust#130553, which replaced an old Clippy infrastructure with a faster one (string matching into symbol matching).
  • Inspections into Clippy's type sizes and cache alignment are being started, but nothing fruitful yet.

Patterns of empty types

  • The linting behavior was reverted until an unspecified date.
  • The next steps are to decide on the future of linting and to write the never patterns RFC.

Provided reasons for yanked crates

  • The PR https://github.com/rust-lang/crates.io/pull/9423 has been merged.
  • Work on the frontend feature is in progress.

Scalable Polonius support on nightly

  • Key developments in the 'Scalable Polonius support on nightly' project include fixing test failures due to off-by-one errors from old mid-points, and ongoing debugging of test failures with a focus on automating the tracing work.
  • Efforts have been made to accept variations of issue #47680, with potential adjustments to active loans computation and locations of effects. Amanda has been cleaning up placeholders in the work-in-progress PR #130227.

Stabilize cargo-script

  • rust-lang/cargo#14404 and rust-lang/cargo#14591 have been addressed.
  • Waiting on time to focus on this in a couple of weeks.

Stabilize parallel front end

  • Key developments: Added the cases in the issue list to the UI test to reproduce the bug or verify the non-reproducibility.
  • Blockers: null.
  • Help wanted: Help test the deadlock code in the issue list and try to reproduce the issue.

Survey tools suitability for Std safety verification

  • Students from the CMU Practicum Project have started writing function contracts that include safety conditions for some unsafe functions in the core library, and verifying that safe abstractions respect those pre-conditions and are indeed safe.
  • Help is needed to write more contracts, integrate new tools, review pull requests, or participate in the repository discussions.

Use annotate-snippets for rustc diagnostic output

  • Progress has been made in matching rustc suggestion output within annotate-snippets, with most cases now aligned.
  • The focus has been on understanding and adapting different rendering styles for suggestions to fit within annotate-snippets.

Goals without updates

The following goals have not received updates in the last month:

Ergonomic ref-counting

Implement "merged doctests" to save doctest time

Completed

Stabilize doc_cfg

Testing infra + contributors for a-mir-formality

User-wide build cache

Continue Reading…

Rust Blog

Announcing Rust 1.82.0

The Rust team is happy to announce a new version of Rust, 1.82.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.82.0 with:

$ rustup update stable

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

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.82.0 stable

cargo info

Cargo now has an info subcommand to display information about a package in the registry, fulfilling a long standing request just shy of its tenth anniversary! Several third-party extensions like this have been written over the years, and this implementation was developed as cargo-information before merging into Cargo itself.

For example, here's what you could see for cargo info cc:

**cc** **#build-dependencies** A build-time dependency for Cargo build scripts to assist in invoking the native C compiler to compile native C code into a static archive to be linked into Rust code. **version:** 1.1.23 **(latest 1.1.30)** **license:** MIT OR Apache-2.0 **rust-version:** 1.63 **documentation:** https://docs.rs/cc **homepage:** https://github.com/rust-lang/cc-rs **repository:** https://github.com/rust-lang/cc-rs **crates.io:** https://crates.io/crates/cc/1.1.23 **features:** jobserver = [] parallel = [dep:libc, dep:jobserver] **note** **:** to see how you depend on cc, run `**cargo tree --invert --package cc@1.1.23**`

By default, cargo info describes the package version in the local Cargo.lock, if any. As you can see, it will indicate when there's a newer version too, and cargo info cc@1.1.30 would report on that.

Apple target promotions

macOS on 64-bit ARM is now Tier 1

The Rust target aarch64-apple-darwin for macOS on 64-bit ARM (M1-family or later Apple Silicon CPUs) is now a tier 1 target, indicating our highest guarantee of working properly. As the platform support page describes, every change in the Rust repository must pass full tests on every tier 1 target before it can be merged. This target was introduced as tier 2 back in Rust 1.49, making it available in rustup. This new milestone puts the aarch64-apple-darwin target on par with the 64-bit ARM Linux and the X86 macOS, Linux, and Windows targets.

Mac Catalyst targets are now Tier 2

Mac Catalyst is a technology by Apple that allows running iOS applications natively on the Mac. This is especially useful when testing iOS-specific code, as cargo test --target=aarch64-apple-ios-macabi --target=x86_64-apple-ios-macabi mostly just works (in contrast to the usual iOS targets, which need to be bundled using external tooling before they can be run on a native device or in the simulator).

The targets are now tier 2, and can be downloaded with rustup target add aarch64-apple-ios-macabi x86_64-apple-ios-macabi, so now is an excellent time to update your CI pipeline to test that your code also runs in iOS-like environments.

Precise capturing use<..> syntax

Rust now supports use<..> syntax within certain impl Trait bounds to control which generic lifetime parameters are captured.

Return-position impl Trait (RPIT) types in Rust capture certain generic parameters. Capturing a generic parameter allows that parameter to be used in the hidden type. That in turn affects borrow checking.

In Rust 2021 and earlier editions, lifetime parameters are not captured in opaque types on bare functions and on functions and methods of inherent impls unless those lifetime parameters are mentioned syntactically in the opaque type. E.g., this is an error:

//@ edition: 2021
fn f(x: &()) -> impl Sized { x }

error[E0700]: hidden type for `impl Sized` captures lifetime that does not appear in bounds
 --> src/main.rs:1:30
  |
1 | fn f(x: &()) -> impl Sized { x }
  |         ---     ----------   ^
  |         |       |
  |         |       opaque type defined here
  |         hidden type `&()` captures the anonymous lifetime defined here
  |
help: add a `use<...>` bound to explicitly capture `'_`
  |
1 | fn f(x: &()) -> impl Sized + use<'_> { x }
  |                            +++++++++

With the new use<..> syntax, we can fix this, as suggested in the error, by writing:

fn f(x: &()) -> impl Sized + use<'_> { x }

Previously, correctly fixing this class of error required defining a dummy trait, conventionally called Captures, and using it as follows:

trait Captures<T: ?Sized> {}
impl<T: ?Sized, U: ?Sized> Captures<T> for U {}

fn f(x: &()) -> impl Sized + Captures<&'_ ()> { x }

That was called "the Captures trick", and it was a bit baroque and subtle. It's no longer needed.

There was a less correct but more convenient way to fix this that was often used called "the outlives trick". The compiler even previously suggested doing this. That trick looked like this:

fn f(x: &()) -> impl Sized + '_ { x }

In this simple case, the trick is exactly equivalent to + use<'_> for subtle reasons explained in RFC 3498. However, in real life cases, this overconstrains the bounds on the returned opaque type, leading to problems. For example, consider this code, which is inspired by a real case in the Rust compiler:

struct Ctx<'cx>(&'cx u8);

fn f<'cx, 'a>(
    cx: Ctx<'cx>,
    x: &'a u8,
) -> impl Iterator<Item = &'a u8> + 'cx {
    core::iter::once_with(move || {
        eprintln!("LOG: {}", cx.0);
        x
    })
//~^ ERROR lifetime may not live long enough
}

We can't remove the + 'cx, since the lifetime is used in the hidden type and so must be captured. Neither can we add a bound of 'a: 'cx, since these lifetimes are not actually related and it won't in general be true that 'a outlives 'cx. If we write + use<'cx, 'a> instead, however, this will work and have the correct bounds.

There are some limitations to what we're stabilizing today. The use<..> syntax cannot currently appear within traits or within trait impls (but note that there, in-scope lifetime parameters are already captured by default), and it must list all in-scope generic type and const parameters. We hope to lift these restrictions over time.

Note that in Rust 2024, the examples above will "just work" without needing use<..> syntax (or any tricks). This is because in the new edition, opaque types will automatically capture all lifetime parameters in scope. This is a better default, and we've seen a lot of evidence about how this cleans up code. In Rust 2024, use<..> syntax will serve as an important way of opting-out of that default.

For more details about use<..> syntax, capturing, and how this applies to Rust 2024, see the "RPIT lifetime capture rules" chapter of the edition guide. For details about the overall direction, see our recent blog post, "Changes to impl Trait in Rust 2024".

Native syntax for creating a raw pointer

Unsafe code sometimes has to deal with pointers that may dangle, may be misaligned, or may not point to valid data. A common case where this comes up are repr(packed) structs. In such a case, it is important to avoid creating a reference, as that would cause undefined behavior. This means the usual & and &mut operators cannot be used, as those create a reference -- even if the reference is immediately cast to a raw pointer, it's too late to avoid the undefined behavior.

For several years, the macros std::ptr::addr_of! and std::ptr::addr_of_mut! have served this purpose. Now the time has come to provide a proper native syntax for this operation: addr_of!(expr) becomes &raw const expr, and addr_of_mut!(expr) becomes &raw mut expr. For example:

#[repr(packed)]
struct Packed {
    not_aligned_field: i32,
}

fn main() {
    let p = Packed { not_aligned_field: 1_82 };

    // This would be undefined behavior!
    // It is rejected by the compiler.
    //let ptr = &p.not_aligned_field as *const i32;

    // This is the old way of creating a pointer.
    let ptr = std::ptr::addr_of!(p.not_aligned_field);

    // This is the new way.
    let ptr = &raw const p.not_aligned_field;

    // Accessing the pointer has not changed.
    // Note that `val = *ptr` would be undefined behavior because
    // the pointer is not aligned!
    let val = unsafe { ptr.read_unaligned() };
}

The native syntax makes it more clear that the operand expression of these operators is interpreted as a place expression. It also avoids the term "address-of" when referring to the action of creating a pointer. A pointer is more than just an address, so Rust is moving away from terms like "address-of" that reaffirm a false equivalence of pointers and addresses.

Safe items with unsafe extern

Rust code can use functions and statics from foreign code. The type signatures of these foreign items are provided in extern blocks. Historically, all items within extern blocks have been unsafe to use, but we didn't have to write unsafe anywhere on the extern block itself.

However, if a signature within the extern block is incorrect, then using that item will result in undefined behavior. Would that be the fault of the person who wrote the extern block, or the person who used that item?

We've decided that it's the responsibility of the person writing the extern block to ensure that all signatures contained within it are correct, and so we now allow writing unsafe extern:

unsafe extern {
    pub safe static TAU: f64;
    pub safe fn sqrt(x: f64) -> f64;
    pub unsafe fn strlen(p: *const u8) -> usize;
}

One benefit of this is that items within an unsafe extern block can be marked as safe to use. In the above example, we can call sqrt or read TAU without using unsafe. Items that aren't marked with either safe or unsafe are conservatively assumed to be unsafe.

In future releases, we'll be encouraging the use of unsafe extern with lints. Starting in Rust 2024, using unsafe extern will be required.

For further details, see RFC 3484 and the "Unsafe extern blocks" chapter of the edition guide.

Unsafe attributes

Some Rust attributes, such as no_mangle, can be used to cause undefined behavior without any unsafe block. If this were regular code we would require them to be placed in an unsafe {} block, but so far attributes have not had comparable syntax. To reflect the fact that these attributes can undermine Rust's safety guarantees, they are now considered "unsafe" and should be written as follows:

#[unsafe(no_mangle)]
pub fn my_global_function() { }

The old form of the attribute (without unsafe) is currently still accepted, but might be linted against at some point in the future, and will be a hard error in Rust 2024.

This affects the following attributes:

  • no_mangle
  • link_section
  • export_name

For further details, see the "Unsafe attributes" chapter of the edition guide.

Omitting empty types in pattern matching

Patterns which match empty (a.k.a. uninhabited) types by value can now be omitted:

use std::convert::Infallible;
pub fn unwrap_without_panic<T>(x: Result<T, Infallible>) -> T {
    let Ok(x) = x; // the `Err` case does not need to appear
    x
}

This works with empty types such as a variant-less enum Void {}, or structs and enums with a visible empty field and no #[non_exhaustive] attribute. It will also be particularly useful in combination with the never type !, although that type is still unstable at this time.

There are some cases where empty patterns must still be written. For reasons related to uninitialized values and unsafe code, omitting patterns is not allowed if the empty type is accessed through a reference, pointer, or union field:

pub fn unwrap_ref_without_panic<T>(x: &Result<T, Infallible>) -> &T {
    match x {
        Ok(x) => x,
        // this arm cannot be omitted because of the reference
        Err(infallible) => match *infallible {},
    }
}

To avoid interfering with crates that wish to support several Rust versions, match arms with empty patterns are not yet reported as “unreachable code” warnings, despite the fact that they can be removed.

Floating-point NaN semantics and const

Operations on floating-point values (of type f32 and f64) are famously subtle. One of the reasons for this is the existence of NaN ("not a number") values which are used to represent e.g. the result of 0.0 / 0.0. What makes NaN values subtle is that more than one possible NaN value exists. A NaN value has a sign (that can be checked with f.is_sign_positive()) and a payload (that can be extracted with f.to_bits()). However, both the sign and payload of NaN values are entirely ignored by == (which always returns false). Despite very successful efforts to standardize the behavior of floating-point operations across hardware architectures, the details of when a NaN is positive or negative and what its exact payload is differ across architectures. To make matters even more complicated, Rust and its LLVM backend apply optimizations to floating-point operations when the exact numeric result is guaranteed not to change, but those optimizations can change which NaN value is produced. For instance, f * 1.0 may be optimized to just f. However, if f is a NaN, this can change the exact bit pattern of the result!

With this release, Rust standardizes on a set of rules for how NaN values behave. This set of rules is not fully deterministic, which means that the result of operations like (0.0 / 0.0).is_sign_positive() can differ depending on the hardware architecture, optimization levels, and the surrounding code. Code that aims to be fully portable should avoid using to_bits and should use f.signum() == 1.0 instead of f.is_sign_positive(). However, the rules are carefully chosen to still allow advanced data representation techniques such as NaN boxing to be implemented in Rust code. For more details on what the exact rules are, check out our documentation.

With the semantics for NaN values settled, this release also permits the use of floating-point operations in const fn. Due to the reasons described above, operations like (0.0 / 0.0).is_sign_positive() (which will be const-stable in Rust 1.83) can produce a different result when executed at compile-time vs at run-time. This is not a bug, and code must not rely on a const fn always producing the exact same result.

Constants as assembly immediates

The const assembly operand now provides a way to use integers as immediates without first storing them in a register. As an example, we implement a syscall towrite by hand:

const WRITE_SYSCALL: c_int = 0x01; // syscall 1 is `write`
const STDOUT_HANDLE: c_int = 0x01; // `stdout` has file handle 1
const MSG: &str = "Hello, world!\n";

let written: usize;

// Signature: `ssize_t write(int fd, const void buf[], size_t count)`
unsafe {
    core::arch::asm!(
        "mov rax, {SYSCALL} // rax holds the syscall number",
        "mov rdi, {OUTPUT}  // rdi is `fd` (first argument)",
        "mov rdx, {LEN}     // rdx is `count` (third argument)",
        "syscall            // invoke the syscall",
        "mov {written}, rax // save the return value",
        SYSCALL = const WRITE_SYSCALL,
        OUTPUT = const STDOUT_HANDLE,
        LEN = const MSG.len(),
        in("rsi") MSG.as_ptr(), // rsi is `buf *` (second argument)
        written = out(reg) written,
    );
}

assert_eq!(written, MSG.len());

Output:

Hello, world!

Playground link.

In the above, a statement such as LEN = const MSG.len() populates the format specifier LEN with an immediate that takes the value of MSG.len(). This can be seen in the generated assembly (the value is 14):

lea     rsi, [rip + .L__unnamed_3]
mov     rax, 1    # rax holds the syscall number
mov     rdi, 1    # rdi is `fd` (first argument)
mov     rdx, 14   # rdx is `count` (third argument)
syscall # invoke the syscall
mov     rax, rax  # save the return value

See the referencefor more details.

Safely addressing unsafe statics

This code is now allowed:

static mut STATIC_MUT: Type = Type::new();
extern "C" {
    static EXTERN_STATIC: Type;
}
fn main() {
     let static_mut_ptr = &raw mut STATIC_MUT;
     let extern_static_ptr = &raw const EXTERN_STATIC;
}

In an expression context, STATIC_MUT and EXTERN_STATIC are place expressions. Previously, the compiler's safety checks were not aware that the raw ref operator did not actually affect the operand's place, treating it as a possible read or write to a pointer. No unsafety is actually present, however, as it just creates a pointer.

Relaxing this may cause problems where some unsafe blocks are now reported as unused if you deny the unused_unsafe lint, but they are now only useful on older versions. Annotate these unsafe blocks with #[allow(unused_unsafe)] if you wish to support multiple versions of Rust, as in this example diff:

 static mut STATIC_MUT: Type = Type::new();
 fn main() {
+    #[allow(unused_unsafe)]
     let static_mut_ptr = unsafe { std::ptr::addr_of_mut!(STATIC_MUT) };
 }

A future version of Rust is expected to generalize this to other expressions which would be safe in this position, not just statics.

Stabilized APIs

These APIs are now stable in const contexts:

Other changes

Check out everything that changed in Rust, Cargo, and Clippy.

Contributors to 1.82.0

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

Continue Reading…

Rust Blog

WebAssembly targets: change in default target-features

The Rust compiler has recently upgraded to using LLVM 19 and this change accompanies some updates to the default set of target features enabled for WebAssembly targets of the Rust compiler. Beta Rust today, which will become Rust 1.82 on 2024-10-17, reflects all of these changes and can be used for testing.

WebAssembly is an evolving standard where extensions are being added over time through a proposals process. WebAssembly proposals reach maturity, get merged into the specification itself, get implemented in engines, and remain this way for quite some time before producer toolchains (e.g. LLVM) update to enable these sufficiently-mature proposals by default. In LLVM 19 this has happened with the multi-value and reference-types proposals for the LLVM/Rust target features multivalue andreference-types. These are now enabled by default in LLVM and transitively means that it's enabled by default for Rust as well.

WebAssembly targets for Rust now have improved documentation about WebAssembly proposals and their corresponding target features. This post is going to review these changes and go into depth about what's changing in LLVM.

WebAssembly Proposals and Compiler Target Features

WebAssembly proposals are the formal means by which the WebAssembly standard itself is evolved over time. Most proposals need toolchain integration in one form or another, for example new flags in LLVM or the Rust compiler. The-Ctarget-feature=... mechanism is used to implement this today. This is a signal to LLVM and the Rust compiler which WebAssembly proposals are enabled or disabled.

There is a loose coupling between the name of a proposal (often the name of the github repository of the proposal) and the feature name LLVM/Rust use. For example there is the multi-value proposal but a multivaluefeature.

The lifecycle of the implementation of a feature in Rust/LLVM typically looks like:

  1. A new WebAssembly proposal is created in a new repository, for example WebAssembly/foo.
  2. Eventually Rust/LLVM implement the proposal under -Ctarget-feature=+foo
  3. Eventually the upstream proposal is merged into the specification, and WebAssembly/foo becomes an archived repository
  4. Rust/LLVM enable the -Ctarget-feature=+foo feature by default but typically retain the ability to disable it as well.

The reference-types and multivalue target features in Rust are at step (4) here now and this post is explaining the consequences of doing so.

Enabling Reference Types by Default

The reference-types proposal to WebAssembly introduced a few new concepts to WebAssembly, notably the externref type which is a host-defined GC resource that WebAssembly cannot access but can pass around. Rust does not have support for the WebAssembly externref type and LLVM 19 does not change that. WebAssembly modules produced from Rust will continue to not use the externref type nor have a means of being able to do so. This may be enabled in the future (e.g. a hypothetical core::arch::wasm32::Externref type or similar), but it will mostly likely only be done on an opt-in basis and will not affect preexisting code by default.

Also included in the reference-types proposal, however, was the ability to have multiple WebAssembly tables in a single module. In the original version of the WebAssembly specification only a single table was allowed and this restriction was relaxed with the reference-types proposal. WebAssembly tables are used by LLVM and Rust to implement indirect function calls. For example function pointers in WebAssembly are actually table indices and indirect function calls are a WebAssembly call_indirect instruction with this table index.

With the reference-types proposal the binary encoding of call_indirectinstructions was updated. Prior to the reference-types proposal call_indirectwas encoded with a fixed zero byte in its instruction (required to be exactly 0x00). This fixed zero byte was relaxed to a 32-bit LEB to indicate which table the call_indirect instruction was using. For those unfamiliar LEB is a way of encoding multi-byte integers in a smaller number of bytes for smaller integers. For example the 32-bit integer 0 can be encoded as 0x00 with aLEB. LEBs are flexible to additionally allow "overlong" encodings so the integer 0 can additionally be encoded as 0x80 0x00.

LLVM's support of separate compilation of source code to a WebAssembly binary means that when an object file is emitted it does not know the final index of the table that is going to be used in the final binary. Before reference-types there was only one option, table 0, so 0x00 was always used when encodingcall_indirect instructions. After reference-types, however, LLVM will emit an over-long LEB of the form 0x80 0x80 0x80 0x80 0x00 which is the maximal length of a 32-bit LEB. This LEB is then filled in by the linker with a relocation to the actual table index that is used by the final module.

When putting all of this together, it means that with LLVM 19, which has the reference-types feature enabled by default, any WebAssembly module with an indirect function call (which is almost always the case for Rust code) will produce a WebAssembly binary that cannot be decoded by engines and tooling that do not support the reference-types proposal. It is expected that this change will have a low impact due to the age of the reference-types proposal and breadth of implementation in engines. Given the multitude of WebAssembly engines, however, it's recommended that any WebAssembly users test out Rust 1.82 beta and see if the produced module still runs on their engine of choice.

LLVM, Rust, and Multiple Tables

One interesting point worth mentioning is that despite the reference-types proposal enabling multiple tables in WebAssembly modules this is not actually taken advantage of at this time by either LLVM or Rust. WebAssembly modules emitted will still have at most one table of functions. This means that the over-long 5-byte encoding of index 0 as 0x80 0x80 0x80 0x80 0x00 is not actually necessary at this time. LLD, LLVM's linker for WebAssembly, wants to process all LEB relocations in a similar manner which currently forces this 5-byte encoding of zero. For example when a function calls another function thecall instruction encodes the target function index as a 5-byte LEB which is filled in by the linker. There is quite often more than one function so the 5-byte encoding enables all possible function indices to be encoded.

In the future LLVM might start using multiple tables as well. For example LLVM may have a mode in the future where there's a table-per-function type instead of a single heterogenous table. This can enable engines to implementcall_indirect more efficiently. This is not implemented at this time, however.

For users who want a minimally-sized WebAssembly module (e.g. if you're in a web context and sending bytes over the wire) it's recommended to use an optimization tool such as wasm-opt to shrink the size of the output of LLVM. Even before this change with reference-types it's recommended to do this as wasm-opt can typically optimize LLVM's default output even further. When optimizing a module through wasm-opt these 5-byte encodings of index 0 are all shrunk to a single byte.

Enabling Multi-Value by Default

The second feature enabled by default in LLVM 19 is multivalue. Themulti-value proposal to WebAssembly enables functions to have more than one return value for example. WebAssembly instructions are additionally allowed to have more than one return value as well. This proposal is one of the first to get merged into the WebAssembly specification after the original MVP and has been implemented in many engines for quite some time.

The consequences of enabling this feature by default in LLVM are more minor for Rust, however, than enabling the reference-types feature by default. LLVM's default C ABI for WebAssembly code is not changing even when multivalue is enabled. Additionally Rust's extern "C" ABI for WebAssembly is not changing either and continues to match LLVM's (or strives to, differences to LLVM are considered bugs to fix). Despite this though the change has the possibility of still affecting Rust users.

Rust for some time has supported an extern "wasm" ABI on Nightly which was an experimental means of exposing the ability of defining a function in Rust which returned multiple values (e.g. used the multi-value proposal). Due to infrastructural changes and refactorings in LLVM itself this feature of Rust hasbeen removed and is no longer supported on Nightly at all. As a result there is no longer any possible method of writing a function in Rust that returns multiple values at the WebAssembly function type level.

In summary this change is expected to not affect any Rust code in the wild unless you were using the Nightly feature of extern "wasm" in which case you'll be forced to drop support for that and use extern "C" instead. Supporting WebAssembly multi-return functions in Rust is a broader topic than this post can cover, but at this time it's an area that's ripe for contribution from suitably motivated contributors.

Aside: ABI Stability and WebAssembly

While on the topic of ABIs and the multivalue feature it's perhaps worth also going over a bit what ABIs mean for WebAssembly. The current definition of the extern "C" ABI for WebAssembly is documented in the tool-conventions repositoryand this is what Clang implements for C code as well. LLVM implements enough support for lowering to WebAssembly as well to support all of this. The extern "Rust ABI is not stable on WebAssembly, as is the case for all Rust targets, and is subject to change over time. There is no reference documentation at this time for what extern "Rust" is on WebAssembly.

The extern "C" ABI, what C code uses by default as well, is difficult to change because stability is often required across different compiler versions. For example WebAssembly code compiled with LLVM 18 might be expected to work with code compiled by LLVM 20. This means that changing the ABI is a daunting task that requires version fields, explicit markers, etc, to help prevent mismatches.

The extern "Rust" ABI, however, is subject to change over time. A great example of this could be that when the multivalue feature is enabled theextern "Rust" ABI could be redefined to use the multiple-return-values that WebAssembly would then support. This would enable much more efficient returns of values larger than 64-bits. Implementing this would require support in LLVM though which is not currently present.

This all means that actually using multiple-returns in functions, or the WebAssembly feature that the multivalue enables, is still out on the horizon and not implemented. First LLVM will need to implement complete lowering support to generate WebAssembly functions with multiple returns, and then extern "Rust" can be change to use this when fully supported. In the yet-further-still future C code might be able to change, but that will take quite some time due to its cross-version-compatibility story.

Enabling Future Proposals to WebAssembly

This is not the first time that a WebAssembly proposal has gone from off-by-default to on-by-default in LLVM, nor will it be the last. For example LLVM already enables the sign-extension proposal by default which MVP WebAssembly did not have. It's expected that in the not-too-distant future thenontrapping-fp-to-intproposal will likely be enabled by default. These changes are currently not made with strict criteria in mind (e.g. N engines must have this implemented for M years), and there may be breakage that happens.

If you're using a WebAssembly engine that does not support the modules emitted by Rust 1.82 beta and LLVM 19 then your options are:

  • Try seeing if the engine you're using has any updates available to it. You might be using an older version which didn't support a feature but a newer version supports the feature.
  • Open an issue to raise awareness that a change is causing breakage. This could either be done on your engine's repository, the Rust repository, or the WebAssemblytool-conventionsrepository. It's recommended to first search to confirm there isn't already an open issue though.
  • Recompile your code with features disabled, more on this in the next section.

The general assumption behind enabling new features by default is that it's a relatively hassle-free operation for end users while bringing performance benefits for everyone (e.g. nontrapping-fp-to-int will make float-to-int conversions more optimal). If updates end up causing hassle it's best to flag that early on so rollout plans can be adjusted if needed.

Disabling on-by-default WebAssembly proposals

For a variety of reasons you might be motivated to disable on-by-default WebAssembly features: for example maybe your engine is difficult to update or doesn't support a new feature. Disabling on-by-default features is unfortunately not the easiest task. It is notably not sufficient to use-Ctarget-features=-sign-ext to disable a feature for just your own project's compilation because the Rust standard library, shipped in precompiled form, is still compiled with the feature enabled.

To disable on-by-default WebAssembly proposal it's required that you use Cargo's-Zbuild-stdfeature. For example:

$ export RUSTFLAGS=-Ctarget-cpu=mvp
$ cargo +nightly build -Zbuild-std=panic_abort,std --target wasm32-unknown-unknown

This will recompiled the Rust standard library in addition to your own code with the "MVP CPU" which is LLVM's placeholder for all WebAssembly proposals disabled. This will disable sign-ext, reference-types, multi-value, etc.

Continue Reading…

Rust Blog

September Project Goals Update

The Rust project is currently working towards a slate of 26 project goals, with 3 of them designed as Flagship Goals. This post provides selected updates on our progress towards these goals (or, in some cases, lack thereof). The full details for any particular goal are available in its associated tracking issue on the rust-project-goals repository.

Flagship goals

Prepare Rust 2024 Edition (tracked in #117)

The Rust 2024 edition is on track to be stabilized on Nightly by Nov 28 and to reach stable as part of Rust v1.85, to be released Feb 20, 2025.

Over the last month, all the "lang team priority items" have landed and are fully ready for release, including migrations and chapters in the Nightly version of the edition guide:

Overall:

  • 13 items are fully ready for Rust 2024.
  • 10 items are fully implemented but still require documentation.
  • 6 items still need implementation work.

Keep in mind, there will be items that are currently tracked for the edition that will not make it. That's OK, and we still plan to ship the edition on time and without those items.

Async Rust Parity (tracked in #105)

We are generally on track with our marquee features:

  1. Support for async closures is available on Nightly and the lang team arrived at a tentative consensus to keep the existing syntax (written rationale and formal decision are in progress). We issued a call for testing as well which has so far uncovered no issues.
  2. Partial support for return-type notation is available on Nightly with the remainder under review.

In addition, dynamic dispatch for async functions and experimental async drop work both made implementation progress. Async WG reorganization has made no progress.

Read the full details on the tracking issue.

Stabilize features needed by Rust for Linux (tracked in #116)

We have stabilized extended offset_of syntax and agreed to stabilize Pointers to Statics in Constants. Credit to @dingxiangfei2009 for driving these forward. 💜

Implementation work proceeds for arbitrary self types v2, derive smart pointer, and sanitizer support.

RFL on Rust CI is implemented but still waiting on documented policy. The first breakage was detected (and fixed) in #129416. This is the mechanism working as intended, although it would also be useful to better define what to do when breakage occurs.

Selected updates

Begin resolving cargo-semver-checks blockers for merging into cargo (tracked in #104)

@obi1kenobi has been working on laying the groundwork to enable manifest linting in their project. They have set up the ability to test how CLI invocations are interpreted internally, and can now snapshot the output of any CLI invocation over a given workspace. They have also designed the expansion of the CLI and the necessary Trustfall schema changes to support manifest linting. As of the latest update, they have a working prototype of manifest querying, which enables SemVer lints such as detecting the accidental removal of features between releases. This work is not blocked on anything, and while there are no immediate opportunities to contribute, they indicate there will be some in future updates.

Expose experimental LLVM features for automatic differentiation and GPU offloading (tracked in #109)

@ZuseZ4 has been focusing on automatic differentiation in Rust, with their first two upstreaming PRs for the rustc frontend and backend merged, and a third PR covering changes to rustc_codegen_llvm currently under review. They are especially proud of getting a detailed LLVM-IR reproducer from a Rust developer for an Enzyme core issue, which will help with debugging. On the GPU side, @ZuseZ4 is taking advantage of recent LLVM updates to rustc that enable more GPU/offloading work. @ZuseZ4 also had a talk about "When unsafe code is slow - Automatic Differentiation in Rust" accepted for the upcoming LLVM dev meeting, where they'll present benchmarks and analysis comparing Rust-Enzyme to the C++ Enzyme frontend.

Extend pubgrub to match cargo's dependency resolution (tracked in #110)

@Eh2406 has achieved the milestone of having the new PubGrub resolver and the existing Cargo resolver accept each other's solutions for all crate versions on crates.io, which involved fixing many bugs related to optional dependencies. Significant progress has also been made in speeding up the resolution process, with over 30% improvements to the average performance of the new resolver, and important changes to allow the existing Cargo resolver to run in parallel. They have also addressed some corner cases where the existing resolver would not accept certain records, and added a check for cyclic dependencies. The latest updates focus on further performance improvements, with the new resolver now taking around 3 hours to process all of crates.io, down from 4.3 hours previously, and a 27% improvement in verifying lock files for non-pathological cases.

Optimizing Clippy & linting

@blyxyas has been working on improving Clippy, the Rust linting tool, with a focus on performance. They have completed a medium-sized objective to use ControlFlow in more places, and have integrated a performance-related issue into their project. A performance-focused PR has also been merged, and they are remaking their benchmarking tool (benchv2) to help with ongoing efforts. The main focus has been on resolving rust-lang/rust#125116, which is now all green after some work. Going forward, they are working on moving the declare_clippy_lint macro to a macro_rules implementation, and have one open proposal-level issue with the performance project label. There are currently no blockers to their work.

Completed goals

The following goals have been completed:

Stalled or orphaned goals

Several goals appear to have stalled or not received updates:

One goal is still waiting for an owner:

Conclusion

This is a brief summary of the progress towards our a subset of the 2024 project goals. There is a lot more information available on the website, including the motivation for each goal, as well as detailed status updates. If you'd like more detail, please do check it out! You can also subscribe to individual tracking issues (or the entire rust-project-goals repo) to get regular updates.

The current set of goals target the second half of 2024 (2024H2). Next month we also expect to begin soliciting goals for the first half of 2025 (2025H1).

Continue Reading…

Rust Blog

Changes to `impl Trait` in Rust 2024

The default way impl Trait works in return position is changing in Rust 2024. These changes are meant to simplify impl Trait to better match what people want most of the time. We're also adding a flexible syntax that gives you full control when you need it.

TL;DR

Starting in Rust 2024, we are changing the rules for when a generic parameter can be used in the hidden type of a return-position impl Trait:

  • a new default that the hidden types for a return-position impl Trait can use any generic parameter in scope, instead of only types (applicable only in Rust 2024);
  • a syntax to declare explicitly what types may be used (usable in any edition).

The new explicit syntax is called a "use bound": impl Trait + use<'x, T>, for example, would indicate that the hidden type is allowed to use 'x and T (but not any other generic parameters in scope).

Read on for the details!

Background: return-position impl Trait

This blog post concerns return-position impl Trait, such as the following example:

fn process_data(
    data: &[Datum]
) -> impl Iterator<Item = ProcessedDatum> {
    data
        .iter()
        .map(|datum| datum.process())
}

The use of -> impl Iterator in return position here means that the function returns "some kind of iterator". The actual type will be determined by the compiler based on the function body. It is called the "hidden type" because callers do not get to know exactly what it is; they have to code against the Iterator trait. However, at code generation time, the compiler will generate code based on the actual precise type, which ensures that callers are fully optimized.

Although callers don't know the exact type, they do need to know that it will continue to borrow the data argument so that they can ensure that the data reference remains valid while iteration occurs. Further, callers must be able to figure this out based solely on the type signature, without looking at the function body.

Rust's current rules are that a return-position impl Trait value can only use a reference if the lifetime of that reference appears in the impl Trait itself. In this example, impl Iterator<Item = ProcessedDatum> does not reference any lifetimes, and therefore capturing data is illegal. You can see this for yourself on the playground.

The error message ("hidden type captures lifetime") you get in this scenario is not the most intuitive, but it does come with a useful suggestion for how to fix it:

help: to declare that
      `impl Iterator<Item = ProcessedDatum>`
      captures `'_`, you can add an
      explicit `'_` lifetime bound
  |
5 | ) -> impl Iterator<Item = ProcessedDatum> + '_ {
  |                                           ++++

Following a slightly more explicit version of this advice, the function signature becomes:

fn process_data<'d>(
    data: &'d [Datum]
) -> impl Iterator<Item = ProcessedDatum> + 'd {
    data
        .iter()
        .map(|datum| datum.process())
}

In this version, the lifetime 'd of the data is explicitly referenced in the impl Trait type, and so it is allowed to be used. This is also a signal to the caller that the borrow for data must last as long as the iterator is in use, which means that it (correctly) flags an error in an example like this (try it on the playground):

let mut data: Vec<Datum> = vec![Datum::default()];
let iter = process_data(&data);
data.push(Datum::default()); // <-- Error!
iter.next();

Usability problems with this design

The rules for what generic parameters can be used in an impl Trait were decided early on based on a limited set of examples. Over time we have noticed a number of problems with them.

not the right default

Surveys of major codebases (both the compiler and crates on crates.io) found that the vast majority of return-position impl trait values need to use lifetimes, so the default behavior of not capturing is not helpful.

not sufficiently flexible

The current rule is that return-position impl trait always allows using type parameters and sometimes allows using lifetime parameters (if they appear in the bounds). As noted above, this default is wrong because most functions actually DO want their return type to be allowed to use lifetime parameters: that at least has a workaround (modulo some details we'll note below). But the default is also wrong because some functions want to explicitly state that they do NOT use type parameters in the return type, and there is no way to override that right now. The original intention was that type alias impl trait would solve this use case, but that would be a very non-ergonomic solution (and stabilizing type alias impl trait is taking longer than anticipated due to other complications).

hard to explain

Because the defaults are wrong, these errors are encountered by users fairly regularly, and yet they are also subtle and hard to explain (as evidenced by this post!). Adding the compiler hint to suggest + '_ helps, but it's not great that users have to follow a hint they don't fully understand.

incorrect suggestion

Adding a + '_ argument to impl Trait may be confusing, but it's not terribly difficult. Unfortunately, it's often the wrong annotation, leading to unnecessary compiler errors -- and the right fix is either complex or sometimes not even possible. Consider an example like this:

fn process<'c, T> {
    context: &'c Context,
    data: Vec<T>,
) -> impl Iterator<Item = ()> + 'c {
    data
        .into_iter()
        .map(|datum| context.process(datum))
}

Here the process function applies context.process to each of the elements in data (of type T). Because the return value uses context, it is declared as + 'c. Our real goal here is to allow the return type to use 'c; writing + 'c achieves that goal because 'c now appears in the bound listing. However, while writing + 'c is a convenient way to make 'c appear in the bounds, also means that the hidden type must outlive 'c. This requirement is not needed and will in fact lead to a compilation error in this example (try it on the playground).

The reason that this error occurs is a bit subtle. The hidden type is an iterator type based on the result of data.into_iter(), which will include the type T. Because of the + 'c bound, the hidden type must outlive 'c, which in turn means that T must outlive 'c. But T is a generic parameter, so the compiler requires a where-clause like where T: 'c. This where-clause means "it is safe to create a reference with lifetime 'c to the type T". But in fact we don't create any such reference, so the where-clause should not be needed. It is only needed because we used the convenient-but-sometimes-incorrect workaround of adding + 'c to the bounds of our impl Trait.

Just as before, this error is obscure, touching on the more complex aspects of Rust's type system. Unlike before, there is no easy fix! This problem in fact occurred frequently in the compiler, leading to an obscure workaround called the Captures trait. Gross!

We surveyed crates on crates.io and found that the vast majority of cases involving return-position impl trait and generics had bounds that were too strong and which could lead to unnecessary errors (though often they were used in simple ways that didn't trigger an error).

inconsistencies with other parts of Rust

The current design was also introducing inconsistencies with other parts of Rust.

async fn desugaring

Rust defines an async fn as desugaring to a normal fn that returns -> impl Future. You might therefore expect that a function like process:

async fn process(data: &Data) { .. }

...would be (roughly) desugared to:

fn process(
    data: &Data
) -> impl Future<Output = ()> {
    async move {
        ..
    }
}

In practice, because of the problems with the rules around which lifetimes can be used, this is not the actual desugaring. The actual desugaring is to a special kind of impl Trait that is allowed to use all lifetimes. But that form of impl Trait was not exposed to end-users.

impl trait in traits

As we pursued the design for impl trait in traits (RFC 3425), we encountered a number of challenges related to the capturing of lifetimes. In order to get the symmetries that we wanted to work (e.g., that one can write -> impl Future in a trait and impl with the expected effect), we had to change the rules to allow hidden types to use all generic parameters (type and lifetime) uniformly.

Rust 2024 design

The above problems motivated us to take a new approach in Rust 2024. The approach is a combination of two things:

  • a new default that the hidden types for a return-position impl Trait can use any generic parameter in scope, instead of only types (applicable only in Rust 2024);
  • a syntax to declare explicitly what types may be used (usable in any edition).

The new explicit syntax is called a "use bound": impl Trait + use<'x, T>, for example, would indicate that the hidden type is allowed to use 'x and T (but not any other generic parameters in scope).

Lifetimes can now be used by default

In Rust 2024, the default is that the hidden type for a return-position impl Trait values use any generic parameter that is in scope, whether it is a type or a lifetime. This means that the initial example of this blog post will compile just fine in Rust 2024 (try it yourself by setting the Edition in the Playground to 2024):

fn process_data(
    data: &[Datum]
) -> impl Iterator<Item = ProcessedDatum> {
    data
        .iter()
        .map(|datum| datum.process())
}

Yay!

Impl Traits can include a use<> bound to specify precisely which generic types and lifetimes they use

As a side-effect of this change, if you move code to Rust 2024 by hand (without cargo fix), you may start getting errors in the callers of functions with an impl Trait return type. This is because those impl Trait types are now assumed to potentially use input lifetimes and not only types. To control this, you can use the new use<> bound syntax that explicitly declares what generic parameters can be used by the hidden type. Our experience porting the compiler suggests that it is very rare to need changes -- most code actually works better with the new default.

The exception to the above is when the function takes in a reference parameter that is only used to read values and doesn't get included in the return value. One such example is the following function indices(): it takes in a slice of type &[T] but the only thing it does is read the length, which is used to create an iterator. The slice itself is not needed in the return value:

fn indices<'s, T>(
    slice: &'s [T],
) -> impl Iterator<Item = usize> {
    0 .. slice.len()
}

In Rust 2021, this declaration implicitly says that slice is not used in the return type. But in Rust 2024, the default is the opposite. That means that callers like this will stop compiling in Rust 2024, since they now assume that data is borrowed until iteration completes:

fn main() {
    let mut data = vec![1, 2, 3];
    let i = indices(&data);
    data.push(4); // <-- Error!
    i.next(); // <-- assumed to access `&data`
}

This may actually be what you want! It means you can modify the definition of indices() later so that it actually does include slice in the result. Put another way, the new default continues the impl Trait tradition of retaining flexibility for the function to change its implementation without breaking callers.

But what if it's not what you want? What if you want to guarantee that indices() will not retain a reference to its argument slice in its return value? You now do that by including a use<> bound in the return type to say explicitly which generic parameters may be included in the return type.

In the case of indices(), the return type actually uses none of the generics, so we would ideally write use<>:

fn indices<'s, T>(
    slice: &'s [T],
) -> impl Iterator<Item = usize> + use<> {
    //                             -----
    //             Return type does not use `'s` or `T`
    0 .. slice.len()
}

Implementation limitation. Unfortunately, if you actually try the above example on nightly today, you'll see that it doesn't compile (try it for yourself). That's because use<> bounds have only partially been implemented: currently, they must always include at least the type parameters. This corresponds to the limitations of impl Trait in earlier editions, which always must capture type parameters. In this case, that means we can write the following, which also avoids the compilation error, but is still more conservative than necessary (try it yourself):

fn indices<T>(
    slice: &[T],
) -> impl Iterator<Item = usize> + use<T> {
    0 .. slice.len()
}

This implementation limitation is only temporary and will hopefully be lifted soon! You can follow the current status at tracking issue #130031.

Alternative: 'static bounds. For the special case of capturing no references at all, it is also possible to use a 'static bound, like so (try it yourself):

fn indices<'s, T>(
    slice: &'s [T],
) -> impl Iterator<Item = usize> + 'static {
    //                             -------
    //             Return type does not capture references.
    0 .. slice.len()
}

'static bounds are convenient in this case, particularly given the current implementation limitations around use<> bounds, but use<> bound are more flexible overall, and so we expect them to be used more often. (As an example, the compiler has a variant of indices that returns newtype'd indices I instead of usize values, and it therefore includes a use<I> declaration.)

Conclusion

This example demonstrates the way that editions can help us to remove complexity from Rust. In Rust 2021, the default rules for when lifetime parameters can be used in impl Trait had not aged well. They frequently didn't express what users needed and led to obscure workarounds being required. They led to other inconsistencies, such as between -> impl Future and async fn, or between the semantics of return-position impl Trait in top-level functions and trait functions.

Thanks to editions, we are able to address that without breaking existing code. With the newer rules coming in Rust 2024,

  • most code will "just work" in Rust 2024, avoiding confusing errors;
  • for the code where annotations are required, we now have a more powerful annotation mechanism that can let you say exactly what you need to say.

Appendix: Relevant links

  • Precise capture was proposed in RFC #3617, which left an unresolved question regarding syntax, and its tracking issue was #123432.
  • The unresolved syntax question was resolved in issue #125836, which introduced the + use<> notation used in this post.
  • The implementation limitation is tracked in #130031.

Continue Reading…

Rust Blog

Announcing Rust 1.81.0

The Rust team is happy to announce a new version of Rust, 1.81.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.81.0 with:

$ rustup update stable

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

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.81.0 stable

core::error::Error

1.81 stabilizes the Error trait in core, allowing usage of the trait in#![no_std] libraries. This primarily enables the wider Rust ecosystem to standardize on the same Error trait, regardless of what environments the library targets.

New sort implementations

Both the stable and unstable sort implementations in the standard library have been updated to new algorithms, improving their runtime performance and compilation time.

Additionally, both of the new sort algorithms try to detect incorrect implementations of Ord that prevent them from being able to produce a meaningfully sorted result, and will now panic on such cases rather than returning effectively randomly arranged data. Users encountering these panics should audit their ordering implementations to ensure they satisfy the requirements documented in PartialOrd and Ord.

#[expect(lint)]

1.81 stabilizes a new lint level, expect, which allows explicitly noting that a particular lint should occur, and warning if it doesn't. The intended use case for this is temporarily silencing a lint, whether due to lint implementation bugs or ongoing refactoring, while wanting to know when the lint is no longer required.

For example, if you're moving a code base to comply with a new restriction enforced via a Clippy lint likeundocumented_unsafe_blocks, you can use #[expect(clippy::undocumented_unsafe_blocks)] as you transition, ensuring that once all unsafe blocks are documented you can opt into denying the lint to enforce it.

Clippy also has two lints to enforce the usage of this feature and help with migrating existing attributes:

Lint reasons

Changing the lint level is often done for some particular reason. For example, if code runs in an environment without floating point support, you could use Clippy to lint on such usage with #![deny(clippy::float_arithmetic)]. However, if a new developer to the project sees this lint fire, they need to look for (hopefully) a comment on the deny explaining why it was added. With Rust 1.81, they can be informed directly in the compiler message:

error: floating-point arithmetic detected
 --> src/lib.rs:4:5
  |
4 |     a + b
  |     ^^^^^
  |
  = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#float_arithmetic
  = note: no hardware float support
note: the lint level is defined here
 --> src/lib.rs:1:9
  |
1 | #![deny(clippy::float_arithmetic, reason = "no hardware float support")]
  |         ^^^^^^^^^^^^^^^^^^^^^^^^

Stabilized APIs

These APIs are now stable in const contexts:

Compatibility notes

Split panic hook and panic handler arguments

We have renamed std::panic::PanicInfo to std::panic::PanicHookInfo. The old name will continue to work as an alias, but will result in a deprecation warning starting in Rust 1.82.0.

core::panic::PanicInfo will remain unchanged, however, as this is now a_different type_.

The reason is that these types have different roles:std::panic::PanicHookInfo is the argument to the panic hook in std context (where panics can have an arbitrary payload), whilecore::panic::PanicInfo is the argument to the#[panic_handler] in#![no_std] context (where panics always carry a formatted message). Separating these types allows us to add more useful methods to these types, such asstd::panic::PanicHookInfo::payload_as_str() andcore::panic::PanicInfo::message().

Abort on uncaught panics in extern "C" functions

This completes the transition started in 1.71, which added dedicated "C-unwind" (amongst other -unwind variants) ABIs for when unwinding across the ABI boundary is expected. As of 1.81, the non-unwind ABIs (e.g., "C") will now abort on uncaught unwinds, closing the longstanding soundness problem.

Programs relying on unwinding should transition to using -unwind suffixed ABI variants.

WASI 0.1 target naming changed

Usage of the wasm32-wasi target (which targets WASI 0.1) will now issue a compiler warning and request users switch to the wasm32-wasip1 target instead. Both targets are the same, wasm32-wasi is only being renamed, and this change to the WASI targetis being done to enable removing wasm32-wasi in January 2025.

Fixes CVE-2024-43402

std::process::Command now correctly escapes arguments when invoking batch files on Windows in the presence of trailing whitespace or periods (which are ignored and stripped by Windows).

See more details in the previous announcement of this change.

Other changes

Check out everything that changed in Rust, Cargo, and Clippy.

Contributors to 1.81.0

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

Continue Reading…

Rust Blog

Security advisory for the standard library (CVE-2024-43402)

On April 9th, 2024, the Rust Security Response WG disclosed CVE-2024-24576, where std::process::Command incorrectly escaped arguments when invoking batch files on Windows. We were notified that our fix for the vulnerability was incomplete, and it was possible to bypass the fix when the batch file name had trailing whitespace or periods (which are ignored and stripped by Windows).

The severity of the incomplete fix is low, due to the niche conditions needed to trigger it. Note that calculating the CVSS score might assign a higher severity to this, but that doesn't take into account what is required to trigger the incomplete fix.

The incomplete fix is identified by CVE-2024-43402.

Overview

Refer to the advisory for CVE-2024-24576 for details on the original vulnerability.

To determine whether to apply the cmd.exe escaping rules, the original fix for the vulnerability checked whether the command name ended with .bat or.cmd. At the time that seemed enough, as we refuse to invoke batch scripts with no file extension.

Unfortunately, Windows removes trailing whitespace and periods when parsing file paths. For example, .bat. . is interpreted by Windows as .bat, but our original fix didn't check for that.

Mitigations

If you are affected by this, and you are using Rust 1.77.2 or greater, you can remove the trailing whitespace (ASCII 0x20) and trailing periods (ASCII 0x2E) from the batch file name to bypass the incomplete fix and enable the mitigations.

Rust 1.81.0, due to be released on September 5th 2024, will update the standard library to apply the CVE-2024-24576 mitigations to all batch files invocations, regardless of the trailing chars in the file name.

Affected versions

All Rust versions before 1.81.0 are affected, if your code or one of your dependencies invoke a batch script on Windows with trailing whitespace or trailing periods in the name, and pass untrusted arguments to it.

Acknowledgements

We want to thank Kainan Zhang (@4xpl0r3r) for responsibly disclosing this to us according to the Rust security policy.

We also want to thank the members of the Rust project who helped us disclose the incomplete fix: Chris Denton for developing the fix, Amanieu D'Antras for reviewing the fix; Pietro Albini for writing this advisory; Pietro Albini, Manish Goregaokar and Josh Stone for coordinating this disclosure.

Continue Reading…

Rust Blog

2024 Leadership Council Survey

One of the responsibilities of the leadership council, formed by RFC 3392, is to solicit feedback on a yearly basis from the Project on how we are performing our duties.

Each year, the Council must solicit feedback on whether the Council is serving its purpose effectively from all willing and able Project members and openly discuss this feedback in a forum that allows and encourages active participation from all Project members. To do so, the Council and other Project members consult the high-level duties, expectations, and constraints listed in this RFC and any subsequent revisions thereof to determine if the Council is meeting its duties and obligations.

This is the council's first year, so we are still figuring out the best way to do this. For this year, a short survey was sent out to all@ on June 24th, 2024, ran for two weeks, and we are now presenting aggregated results from the survey. Raw responses will not be shared beyond the leadership council, but the results below reflect sentiments shared in response to each question. We invite feedback and suggestions on actions to take on Zulip or through direct communication to council members.

We want to thank everyone for their feedback! It has been very valuable to hear what people are thinking. As always, if you have thoughts or concerns, please reach out to your council representative any time.

Survey results

We received 53 responses to the survey, representing roughly a 32% response rate (out of 163 current recipients of all@).

Do you feel that the Rust Leadership Council is serving its purpose effectively?

Option

Response count

Strongly agree

1

Agree

18

Unsure

30

Disagree

4

Strongly disagree

0

I am aware of the role that the Leadership Council plays in the governance of the Rust Project.

Option

Response count

Strongly agree

9

Agree

20

Unsure

14

Disagree

7

Strongly disagree

3

The Rust Project has a solid foundation of Project governance.

Option

Response count

Strongly agree

3

Agree

16

Unsure

20

Disagree

11

Strongly disagree

3

Areas that are going well

For the rest of the questions we group responses into rough categories. The number of those responses is also provided; note that some responses may have fallen into more than one of these categories.

  • (5) Less drama
  • (5) More public operations
  • (5) Lack of clarity / knowledge about what it does
    • It's not obvious why this is a "going well" from the responses, but it was given in response to this question.
  • (4) General/inspecific positivity.
  • (2) Improved Foundation/project relations
  • (2) Funding travel/get-togethers of team members
  • (1) Clear representation of members of the Project
  • (1) Turnover while retaining members

Areas that are not going well

  • (15) Knowing what the council is doing
  • (3) Not enough delegation of decisions
  • (2) Finding people interested in being on the council / helping the council
  • (1) What is the role of the project directors? Are they redundant given the council?
  • (2) Too conservative in trying things / decisions/progress is made too slowly.
  • (1) Worry over Foundation not trusting Project

Suggestions for things to do in the responses:

  • (2) Addressing burnout
  • (2) More social time between teams
  • (2) More communication/accountability with/for the Foundation
  • (2) Hiring people, particularly for non-technical roles
  • (1) Helping expand the moderation team
  • (1) Resolving the launching pad issues, e.g., through "Rust Society" work
  • (1) Product management for language/compiler/libraries

Takeaways for future surveys

  • We should structure the survey to specifically ask about high-level duties and/or enumerate areas of interest (e.g., numeric responses on key questions like openness and effectiveness)
  • Consider linking published material/writing 1-year retrospective and that being linked from the survey as pre-reading.
  • We should disambiguate between neutral and "not enough information/knowledge to answer" responses in multiple choice response answers.

Proposed action items

We don't have any concrete proposed actions at this time, though are interested in finding ways to have more visilibity for council activities, as that seems to be one of the key problems called out across all of the questions asked. How exactly to achieve this remains unclear though.

As mentioned earlier, we welcome input from the community on suggestions for both improving this process and for actions to change how the council operates.

Continue Reading…

Rust Blog

Rust Project goals for 2024

With the merging of RFC #3672, the Rust project has selected a slate of 26 Project Goals for the second half of 2024 (2024H2). This is our first time running an experimental new roadmapping process; assuming all goes well, we expect to be running the process roughly every six months. Of these goals, we have designated three of them as our flagship goals, representing our most ambitious and most impactful efforts: (1) finalize preparations for the Rust 2024 edition; (2) bring the Async Rust experience closer to parity with sync Rust; and (3) resolve the biggest blockers to the Linux kernel building on stable Rust. As the year progresses we'll be posting regular updates on these 3 flagship goals along with the 23 others.

Rust’s mission

All the goals selected ultimately further Rust's mission of empowering everyone to build reliable and efficient software. Rust targets programs that prioritize

  • reliability and robustness;
  • performance, memory usage, and resource consumption; and
  • long-term maintenance and extensibility.

We consider "any two out of the three" to be the right heuristic for projects where Rust is a strong contender or possibly the best option, and we chose our goals in part so as to help ensure this is true.

Why these particular flagship goals?

2024 Edition. 2024 will mark the 4th Rust edition, following on the 2015, 2018, and 2021 editions. Similar to the 2021 edition, the 2024 edition is not a "major marketing push" but rather an opportunity to correct small ergonomic issues with Rust that will make it overall much easier to use. The changes planned for the 2024 edition include (1) supporting -> impl Trait and async fn in traits by aligning capture behavior; (2) permitting (async) generators to be added in the future by reserving the gen keyword; and (3) altering fallback for the ! type. The plan is to finalize development of 2024 features this year; the Edition itself is planned for Rust v1.85 (to be released to beta 2025-01-03 and to stable on 2025-02-20).

Async. In 2024 we plan to deliver several critical async Rust building block features, most notably support for async closures and Send bounds. This is part of a multi-year program aiming to raise the experience of authoring "async Rust" to the same level of quality as "sync Rust". Async Rust is widely used, with 52% of the respondents in the 2023 Rust survey indicating that they use Rust to build server-side or backend applications.

Rust for Linux. The experimental support for Rust development in the Linux kernel is a watershed moment for Rust, demonstrating to the world that Rust is indeed capable of targeting all manner of low-level systems applications. And yet today that support rests on a number of unstable features, blocking the effort from ever going beyond experimental status. For 2024H2 we will work to close the largest gaps that block support.

Highlights from the other goals

In addition to the flagship goals, the roadmap defines 23 other goals. Here is a subset to give you a flavor:

Check out the whole list! (Go ahead, we'll wait, but come back here afterwards!)

How to track progress

As the year progresses, we will be posting regular blog posts summarizing the progress on the various goals. If you'd like to see more detail, the 2024h2 milestone on the rust-lang/rust-project-goals repository has tracking issues for each of the goals. Each issue is assigned to the owner(s) of that particular goal. You can subscribe to the issue to receive regular updates, or monitor the #project-goals channel on the rust-lang Zulip. Over time we will likely create other ways to follow along, such as a page on rust-lang.org to visualize progress (if you'd like to help with that, reach out to @nikomatsakis, thanks!).

It's worth stating up front: we don't expect all of these goals to be completed. Many of them were proposed and owned by volunteers, and it's normal and expected that things don't always work out as planned. In the event that a goal seems to stall out, we can either look for a new owner or just consider the goal again in the next round of goal planning.

How we selected project goals

Each project goal began as a PR against the rust-lang/rust-project-goals repository. As each PR came in, the goals were socialized with the teams. This process sometimes resulted in edits to the goals or in breaking up larger goals into smaller chunks (e.g., a far-reaching goal for "higher level Rust" was broken into two specific deliverables, a user-wide build cache and ergonomic ref counting). Finally, the goals were collated into RFC #3672, which listed each goals as well as all the asks from the team. This RFC was approved by all the teams that are being asked for support or other requests.

Conclusion: Project Goals as a "front door" for Rust

To me, the most exciting thing about the Project Goals program has been seeing the goals coming from outside the existing Rust maintainers. My hope is that the Project Goal process can supplement RFCs as an effective "front door" for the project, offering people who have the resources and skill to drive changes a way to float that idea and get feedback from the Rust teams before they begin to work on it.

Project Goals also help ensure the sustainability of the Rust open source community. In the past, it was difficult to tell when starting work on a project whether it would be well-received by the Rust maintainers. This was an obstacle for those who would like to fund efforts to improve Rust, as people don't like to fund work without reasonable confidence it will succeed. Project goals are a way for project maintainers to "bless" a particular project and indicate their belief that it will be helpful to Rust. The Rust Foundation is using project goals as one of their criteria when considering fellowship applications, for example, and I expect over time other grant programs will do the same. But project goals are useful for others, too: having an approved project goal can help someone convince their employer to give them time to work on Rust open source efforts, for example, or give contractors the confidence they need to ensure their customer they'll be able to get the work done.

The next round of goal planning will be targeting 2025H1 and is expected to start in October. We look forward to seeing what great ideas are proposed!

Continue Reading…

Rust Blog

Announcing Rust 1.80.1

The Rust team has published a new point release of Rust, 1.80.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.80.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.80.1

Rust 1.80.1 fixes two regressions that were recently reported.

Miscompilation when comparing floats

In addition to the existing optimizations performed by LLVM, rustc is growing its own set of optimizations. Rust 1.78.0 added a new one, implementing "jump threading" (merging together two adjacent branches that perform the same comparison).

The optimization was also enabled on branches checking for floating point equality, but it didn't implement the special rules needed for floats comparison (NaN != NaN and 0.0 == -0.0). This caused the optimization to miscompile code performing those checks.

Rust 1.80.1 addresses the problem by preventing the optimization from being applied to float comparisons, while retaining the optimization on other supported types.

False positives in the dead_code lint

Rust 1.80.0 contained refactorings to the dead_code lint. We received multiple reports that the new lint implementation produces false positives, so we are reverting the changes in Rust 1.80.1. We'll continue to experiment on how to improve the accuracy of dead_code in future releases.

Contributors to 1.80.1

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

Continue Reading…

Rust Blog

Rust participates in OSPP 2024

Similar to our previous announcements of the Rust Project's participation in Google Summer of Code (GSoC), we are now announcing our participation in Open Source Promotion Plan (OSPP) 2024.

OSPP is a program organized in large part by The Institute of Software Chinese Academy of Sciences. Its goal is to encourage college students to participate in developing and maintaining open source software. The Rust Project is already registered and has a number of projects available for mentorship:

Eligibility is limited to students and there is a guide for potential participants. Student registration ends on the 3rd of June with the project application deadline a day later.

Unlike GSoC which allows students to propose their own projects, OSPP requires that students only apply for one of the registered projects. We do have an #ospp Zulip stream and potential contributors are encouraged to join and discuss details about the projects and connect with mentors.

After the project application window closes on June 4th, we will review and select participants, which will be announced on June 26th. From there, students will participate through to the end of September.

As with GSoC, this is our first year participating in this program. We are incredibly excited for this opportunity to further expand into new open source communities and we're hopeful for a productive and educational summer.

Continue Reading…

Rust Blog

Automatic checking of cfgs at compile-time

The Cargo and Compiler team are delighted to announce that starting with Rust 1.80 (or nightly-2024-05-05) every reachable #[cfg] will be automatically checked that they match the expected config names and values.

This can help with verifying that the crate is correctly handling conditional compilation for different target platforms or features. It ensures that the cfg settings are consistent between what is intended and what is used, helping to catch potential bugs or errors early in the development process.

This addresses a common pitfall for new and advanced users.

This is another step to our commitment to provide user-focused tooling and we are eager and excited to finally see it fixed, after more than two years since the original RFC 30131.

A look at the feature

Every time a Cargo feature is declared that feature is transformed into a config that is passed to rustc (the Rust compiler) so it can verify with it along with well known cfgs if any of the #[cfg], #![cfg_attr] and cfg! have unexpected configs and report a warning with the unexpected_cfgs lint.

Cargo.toml:

[package]
name = "foo"

[features]
lasers = []
zapping = []

src/lib.rs:

#[cfg(feature = "lasers")]  // This condition is expected
                            // as "lasers" is an expected value
                            // of the `feature` cfg
fn shoot_lasers() {}

#[cfg(feature = "monkeys")] // This condition is UNEXPECTED
                            // as "monkeys" is NOT an expected
                            // value of the `feature` cfg
fn write_shakespeare() {}

#[cfg(windosw)]             // This condition is UNEXPECTED
                            // it's supposed to be `windows`
fn win() {}

cargo check:

cargo-check

Custom cfgs and build scripts

In Cargo point-of-view: a custom cfg is one that is neither defined by rustc nor by a Cargo feature. Think of tokio_unstable, has_foo, ... but not feature = "lasers", unix or debug_assertions

Some crates use custom cfgs that they either expected from the environment (RUSTFLAGSor other means) or is enabled by some logic in the crate build.rs. For those crates Cargo provides a new instruction: cargo::rustc-check-cfg2 (or cargo:rustc-check-cfg for older Cargo version).

The syntax to use is described in the rustc book section checking configuration, but in a nutshell the basic syntax of --check-cfg is:

cfg(name, values("value1", "value2", ..., "valueN"))

Note that every custom cfgs must always be expected, regardless if the cfg is active or not!

build.rs example

build.rs:

fn main() {
    println!("cargo::rustc-check-cfg=cfg(has_foo)");
    //        ^^^^^^^^^^^^^^^^^^^^^^ new with Cargo 1.80
    if has_foo() {
        println!("cargo::rustc-cfg=has_foo");
    }
}

Each cargo::rustc-cfg should have an accompanying unconditional cargo::rustc-check-cfg directive to avoid warnings like this: unexpected cfg condition name: has_foo.

Equivalence table

cargo::rustc-cfg

cargo::rustc-check-cfg

foo

cfg(foo) or cfg(foo, values(none()))

foo=""

cfg(foo, values(""))

foo="bar"

cfg(foo, values("bar"))

foo="1" and foo="2"

cfg(foo, values("1", "2"))

foo="1" and bar="2"

cfg(foo, values("1")) and cfg(bar, values("2"))

foo and foo="bar"

cfg(foo, values(none(), "bar"))

More details can be found in the rustc book.

Frequently asked questions

Can it be disabled?

For Cargo users, the feature is always on and cannot be disabled, but like any other lints it can be controlled: #![warn(unexpected_cfgs)].

Does the lint affect dependencies?

No, like most lints, unexpected_cfgs will only be reported for local packages thanks to cap-lints.

How does it interact with the RUSTFLAGS env?

You should be able to use the RUSTFLAGS environment variable like it was before.Currently --cfg arguments are not checked, only usage in code are.

This means that doing RUSTFLAGS="--cfg tokio_unstable" cargo check will not report any warnings, unless tokio_unstable is used within your local crates, in which case crate author will need to make sure that that custom cfg is expected with cargo::rustc-check-cfg in the build.rs of that crate.

How to expect custom cfgs without a build.rs?

There is currently no way to expect a custom cfg other than with cargo::rustc-check-cfg in a build.rs.

Crate authors that don't want to use a build.rs are encouraged to use Cargo features instead.

How does it interact with other build systems?

Non-Cargo based build systems are not affected by the lint by default. Build system authors that wish to have the same functionality should look at the rustc documentation for the --check-cfg flag for a detailed explanation of how to achieve the same functionality.

  1. The stabilized implementation and RFC 3013 diverge significantly, in particular there is only one form for --check-cfg: cfg() (instead of values() and names() being incomplete and subtlety incompatible with each other).
  2. cargo::rustc-check-cfg will start working in Rust 1.80 (or nightly-2024-05-05). From Rust 1.77 to Rust 1.79 (inclusive) it is silently ignored. In Rust 1.76 and below a warning is emitted when used without the unstable Cargo flag -Zcheck-cfg.

Continue Reading…

Rust Blog

Announcing Rustup 1.27.1

The Rustup team is happy to announce the release of Rustup version 1.27.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.27.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.27.1

This new Rustup release involves some minor bug fixes.

The headlines for this release are:

  1. Prebuilt Rustup binaries should be working on older macOS versions again.
  2. rustup-init will no longer fail when fish is installed but ~/.config/fish/conf.d hasn't been created.
  3. Regressions regarding symlinked RUSTUP_HOME/(toolchains|downloads|tmp) have been addressed.

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.27.1 possible!

  • Anas (0x61nas)
  • cuiyourong (cuiyourong)
  • Dirkjan Ochtman (djc)
  • Eric Huss (ehuss)
  • eth3lbert (eth3lbert)
  • hev (heiher)
  • klensy (klensy)
  • Chih Wang (ongchi)
  • Adam (pie-flavor)
  • rami3l (rami3l)
  • Robert (rben01)
  • Robert Collins (rbtcollins)
  • Sun Bin (shandongbinzhou)
  • Samuel Moelius (smoelius)
  • vpochapuis (vpochapuis)
  • Renovate Bot (renovate)

Continue Reading…

Rust Blog

Announcing Rust 1.78.0

The Rust team is happy to announce a new version of Rust, 1.78.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.78.0 with:

$ rustup update stable

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

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.78.0 stable

Diagnostic attributes

Rust now supports a #[diagnostic] attribute namespace to influence compiler error messages. These are treated as hints which the compiler is not required to use, and it is also not an error to provide a diagnostic that the compiler doesn't recognize. This flexibility allows source code to provide diagnostics even when they're not supported by all compilers, whether those are different versions or entirely different implementations.

With this namespace comes the first supported attribute, #[diagnostic::on_unimplemented], which can be placed on a trait to customize the message when that trait is required but hasn't been implemented on a type. Consider the example given in the stabilization pull request:

#[diagnostic::on_unimplemented(
    message = "My Message for `ImportantTrait<{A}>` is not implemented for `{Self}`",
    label = "My Label",
    note = "Note 1",
    note = "Note 2"
)]
trait ImportantTrait<A> {}

fn use_my_trait(_: impl ImportantTrait<i32>) {}

fn main() {
    use_my_trait(String::new());
}

Previously, the compiler would give a builtin error like this:

error[E0277]: the trait bound `String: ImportantTrait<i32>` is not satisfied
  --> src/main.rs:12:18
   |
12 |     use_my_trait(String::new());
   |     ------------ ^^^^^^^^^^^^^ the trait `ImportantTrait<i32>` is not implemented for `String`
   |     |
   |     required by a bound introduced by this call
   |

With #[diagnostic::on_unimplemented], its custom message fills the primary error line, and its custom label is placed on the source output. The original label is still written as help output, and any custom notes are written as well. (These exact details are subject to change.)

error[E0277]: My Message for `ImportantTrait<i32>` is not implemented for `String`
  --> src/main.rs:12:18
   |
12 |     use_my_trait(String::new());
   |     ------------ ^^^^^^^^^^^^^ My Label
   |     |
   |     required by a bound introduced by this call
   |
   = help: the trait `ImportantTrait<i32>` is not implemented for `String`
   = note: Note 1
   = note: Note 2

For trait authors, this kind of diagnostic is more useful if you can provide a better hint than just talking about the missing implementation itself. For example, this is an abridged sample from the standard library:

#[diagnostic::on_unimplemented(
    message = "the size for values of type `{Self}` cannot be known at compilation time",
    label = "doesn't have a size known at compile-time"
)]
pub trait Sized {}

For more information, see the reference section on the diagnostic tool attribute namespace.

Asserting unsafe preconditions

The Rust standard library has a number of assertions for the preconditions of unsafe functions, but historically they have only been enabled in #[cfg(debug_assertions)] builds of the standard library to avoid affecting release performance. However, since the standard library is usually compiled and distributed in release mode, most Rust developers weren't ever executing these checks at all.

Now, the condition for these assertions is delayed until code generation, so they will be checked depending on the user's own setting for debug assertions -- enabled by default in debug and test builds. This change helps users catch undefined behavior in their code, though the details of how much is checked are generally not stable.

For example, slice::from_raw_parts requires an aligned non-null pointer. The following use of a purposely-misaligned pointer has undefined behavior, and while if you were unlucky it may have appeared to "work" in the past, the debug assertion can now catch it:

fn main() {
    let slice: &[u8] = &[1, 2, 3, 4, 5];
    let ptr = slice.as_ptr();

    // Create an offset from `ptr` that will always be one off from `u16`'s correct alignment
    let i = usize::from(ptr as usize & 1 == 0);
    
    let slice16: &[u16] = unsafe { std::slice::from_raw_parts(ptr.add(i).cast::<u16>(), 2) };
    dbg!(slice16);
}

thread 'main' panicked at library/core/src/panicking.rs:220:5:
unsafe precondition(s) violated: slice::from_raw_parts requires the pointer to be aligned and non-null, and the total size of the slice not to exceed `isize::MAX`
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
thread caused non-unwinding panic. aborting.

Deterministic realignment

The standard library has a few functions that change the alignment of pointers and slices, but they previously had caveats that made them difficult to rely on in practice, if you followed their documentation precisely. Those caveats primarily existed as a hedge against const evaluation, but they're only stable for non-const use anyway. They are now promised to have consistent runtime behavior according to their actual inputs.

  • pointer::align_offset computes the offset needed to change a pointer to the given alignment. It returns usize::MAX if that is not possible, but it was previously permitted to always return usize::MAX, and now that behavior is removed.
  • slice::align_to and slice::align_to_mut both transmute slices to an aligned middle slice and the remaining unaligned head and tail slices. These methods now promise to return the largest possible middle part, rather than allowing the implementation to return something less optimal like returning everything as the head slice.

Stabilized APIs

These APIs are now stable in const contexts:

Compatibility notes

  • As previously announced, Rust 1.78 has increased its minimum requirement to Windows 10 for the following targets:
    • x86_64-pc-windows-msvc
    • i686-pc-windows-msvc
    • x86_64-pc-windows-gnu
    • i686-pc-windows-gnu
    • x86_64-pc-windows-gnullvm
    • i686-pc-windows-gnullvm
  • Rust 1.78 has upgraded its bundled LLVM to version 18, completing the announced u128/i128 ABI change for x86-32 and x86-64 targets. Distributors that use their own LLVM older than 18 may still face the calling convention bugs mentioned in that post.

Other changes

Check out everything that changed in Rust, Cargo, and Clippy.

Contributors to 1.78.0

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

Continue Reading…

Rust Blog

Announcing Google Summer of Code 2024 selected projects

The Rust Project is participating in Google Summer of Code (GSoC) 2024, a global program organized by Google which is designed to bring new contributors to the world of open-source.

In February, we published a list of GSoC project ideas, and started discussing these projects with potential GSoC applicants on our Zulip. We were pleasantly surprised by the amount of people that wanted to participate in these projects and that led to many fruitful discussions with members of various Rust teams. Some of them even immediately began contributing to various repositories of the Rust Project, even before GSoC officially started!

After the initial discussions, GSoC applicants prepared and submitted their project proposals. We received 65 (!) proposals in total. We are happy to see that there was so much interest, given that this is the first time the Rust Project is participating in GSoC.

A team of mentors primarily composed of Rust Project contributors then thoroughly examined the submitted proposals. GSoC required us to produce a ranked list of the best proposals, which was a challenging task in itself since Rust is a big project with many priorities! We went through many rounds of discussions and had to consider many factors, such as prior conversations with the given applicant, the quality and scope of their proposal, the importance of the proposed project for the Rust Project and its wider community, but also the availability of mentors, who are often volunteers and thus have limited time available for mentoring.

In many cases, we had multiple proposals that aimed to accomplish the same goal. Therefore, we had to pick only one per project topic despite receiving several high-quality proposals from people we'd love to work with. We also often had to choose between great proposals targeting different work within the same Rust component to avoid overloading a single mentor with multiple projects.

In the end, we narrowed the list down to twelve best proposals, which we felt was the maximum amount that we could realistically support with our available mentor pool. We submitted this list and eagerly awaited how many of these twelve proposals would be accepted into GSoC.

Selected projects

On the 1st of May, Google has announced the accepted projects. We are happy to announce that 9 proposals out of the twelve that we have submitted were accepted by Google, and will thus participate in Google Summer of Code 2024! Below you can find the list of accepted proposals (in alphabetical order), along with the names of their authors and the assigned mentor(s):

  • Adding lint-level configuration to cargo-semver-checks by Max Carr, mentored by Predrag Gruevski
  • Implementation of a Faster Register Allocator For Cranelift by d-sonuga, mentored by Chris Fallin and Amanieu d'Antras
  • Improve Rust benchmark suite by s7tya, mentored by Jakub Beránek
  • Move cargo shell completions to Rust by shanmu, mentored by Ed Page
  • Rewriting Esoteric, Error-Prone Makefile Tests Using Robust Rust Features by Julien Robert, mentored by Jieyou Xu
  • Rewriting the Rewrite trait by SeoYoung Lee, mentored by Yacin Tmimi
  • Rust to .NET compiler - add support for compiling & running cargo tests by Fractal Fir, mentored by Jack Huey
  • Sandboxed and Deterministic Proc Macro using Wasm by Apurva Mishra, mentored by David Lattimore
  • Tokio async support in Miri by Tiffany Pek Yuan, mentored by Oli Scherer

Congratulations to all applicants whose project was selected! The mentors are looking forward to working with you on these exciting projects to improve the Rust ecosystem. You can expect to hear from us soon, so that we can start coordinating the work on your GSoC projects.

We would also like to thank all the applicants whose proposal was sadly not accepted, for their interactions with the Rust community and contributions to various Rust projects. There were some great proposals that did not make the cut, in large part because of limited review capacity. However, even if your proposal was not accepted, we would be happy if you would consider contributing to the projects that got you interested, even outside GSoC! Our project idea list is still actual, and could serve as a general entry point for contributors that would like to work on projects that would help the Rust Project maintainers and the Rust ecosystem.

Assuming our involvement in GSoC 2024 is successful, there's a good chance we'll participate next year as well (though we can't promise anything yet) and we hope to receive your proposals again in the future! We also are planning to participate in similar programs in the very near future. Those announcements will come in separate blog posts, so make sure to subscribe to this blog so that you don't miss anything.

The accepted GSoC projects will run for several months. After GSoC 2024 finishes (in autumn of 2024), we plan to publish a blog post in which we will summarize the outcome of the accepted projects.

Continue Reading…

Rust Blog

Security advisory for the standard library (CVE-2024-24576)

The Rust Security Response WG was notified that the Rust standard library did not properly escape arguments when invoking batch files (with the bat andcmd extensions) on Windows using the Command API. An attacker able to control the arguments passed to the spawned process could execute arbitrary shell commands by bypassing the escaping.

The severity of this vulnerability is critical if you are invoking batch files on Windows with untrusted arguments. No other platform or use is affected.

This vulnerability is identified by CVE-2024-24576.

Overview

The Command::arg and Command::args APIs state in their documentation that the arguments will be passed to the spawned process as-is, regardless of the content of the arguments, and will not be evaluated by a shell. This means it should be safe to pass untrusted input as an argument.

On Windows, the implementation of this is more complex than other platforms, because the Windows API only provides a single string containing all the arguments to the spawned process, and it's up to the spawned process to split them. Most programs use the standard C run-time argv, which in practice results in a mostly consistent way arguments are splitted.

One exception though is cmd.exe (used among other things to execute batch files), which has its own argument splitting logic. That forces the standard library to implement custom escaping for arguments passed to batch files. Unfortunately it was reported that our escaping logic was not thorough enough, and it was possible to pass malicious arguments that would result in arbitrary shell execution.

Mitigations

Due to the complexity of cmd.exe, we didn't identify a solution that would correctly escape arguments in all cases. To maintain our API guarantees, we improved the robustness of the escaping code, and changed the Command API to return an InvalidInput error when it cannot safely escape an argument. This error will be emitted when spawning the process.

The fix will be included in Rust 1.77.2, to be released later today.

If you implement the escaping yourself or only handle trusted inputs, on Windows you can also use the CommandExt::raw_arg method to bypass the standard library's escaping logic.

Affected Versions

All Rust versions before 1.77.2 on Windows are affected, if your code or one of your dependencies executes batch files with untrusted arguments. Other platforms or other uses on Windows are not affected.

Acknowledgments

We want to thank RyotaK for responsibly disclosing this to us according to theRust security policy, and Simon Sawicki (Grub4K) for identifying some of the escaping rules we adopted in our fix.

We also want to thank the members of the Rust project who helped us disclose the vulnerability: Chris Denton for developing the fix; Mara Bos for reviewing the fix; Pietro Albini for writing this advisory; Pietro Albini, Manish Goregaokar and Josh Stone for coordinating this disclosure; Amanieu d'Antras for advising during the disclosure.

Continue Reading…

Rust Blog

Announcing Rust 1.77.2

The Rust team has published a new point release of Rust, 1.77.2. 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.77.2 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.77.2

This release includes a fix for CVE-2024-24576.

Before this release, the Rust standard library did not properly escape arguments when invoking batch files (with the bat and cmd extensions) on Windows using the Command API. An attacker able to control the arguments passed to the spawned process could execute arbitrary shell commands by bypassing the escaping.

This vulnerability is CRITICAL if you are invoking batch files on Windows with untrusted arguments. No other platform or use is affected.

You can learn more about the vulnerability in the dedicated advisory.

Contributors to 1.77.2

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

Continue Reading…

Rust Blog

Changes to Rust's WASI targets

WASI 0.2 was recently stabilized, and Rust has begun implementing first-class support for it in the form of a dedicated new target. Rust 1.78 will introduce new wasm32-wasip1 (tier 2) and wasm32-wasip2 (tier 3) targets. wasm32-wasip1 is an effective rename of the existing wasm32-wasitarget, freeing the target name up for an eventual WASI 1.0 release. Starting Rust 1.78 (May 2nd, 2024), users of WASI 0.1 are encouraged to begin migrating to the new wasm32-wasip1 target before the existing wasm32-wasi target is removed in Rust 1.84 (January 5th, 2025).

In this post we'll discuss the introduction of the new targets, the motivation behind it, what that means for the existing WASI targets, and a detailed schedule for these changes. This post is about the WASI targets only; the existing wasm32-unknown-unknown and wasm32-unknown-emscripten targets are unaffected by any changes in this post.

Introducing wasm32-wasip2

After nearly five years of work the WASI 0.2 specificationwas recently stabilized. This work builds on WebAssembly Components (think: strongly-typed ABI for Wasm), providing standard interfaces for things like asynchronous IO, networking, and HTTP. This will finally make it possible to write asynchronous networked services on top of WASI, something which wasn't possible using WASI 0.1.

People interested in compiling Rust code to WASI 0.2 today are able to do so using the cargo-componenttool. This tool is able to take WASI 0.1 binaries, and transform them to WASI 0.2 Components using a shim. It also provides native support for common cargo commands such as cargo build, cargo test, and cargo run. While it introduces some inefficiencies because of the additional translation layer, in practice this already works really well and people should be enough able to get started with WASI 0.2 development.

We're however keen to begin making that translation layer obsolete. And for that reason we're happy to share that Rust has made its first steps towards that with the introduction of the tier 3 wasm32-wasip2target landing in Rust 1.78. This will initially miss a lot of expected features such as stdlib support, and we don't recommend people use this target quite yet. But as we fill in those missing features over the coming months, we aim to eventually hit meet the criteria to become a tier 2 target, at which point the wasm32-wasip2 target would be considered ready for general use. This work will happen through 2024, and we expect for this to land before the end of the calendar year.

Renaming wasm32-wasi to wasm32-wasip1

The original name for what we now call WASI 0.1 was "WebAssembly System Interface, snapshot 1". Rust shipped support for this in 2019, and we did so knowing the target would likely undergo significant changes in the future. With the knowledge we have today though, we would not have chosen to introduce the "WASI, snapshot 1" target as wasm32-wasi. We should have instead chosen to add some suffix to the initial target triple so that the eventual stable WASI 1.0 target can just be called wasm32-wasi.

In anticipation of both an eventual WASI 1.0 target, and to preserve consistency between target names, we'll begin rolling out a name change to the existing WASI 0.1 target. Starting in Rust 1.78 (May 2nd, 2024) a new wasm32-wasip1 target will become available. Starting Rust 1.81 (September 5th, 2024) we will begin warning existing users of wasm32-wasi to migrate to wasm32-wasip1. And finally in Rust 1.84 (January 9th, 2025) the wasm32-wasi target will no longer be shipped on the stable release channel. This will provide an 8 month transition period for projects to switch to the new target name when they update their Rust toolchains.

The name wasip1 can be read as either "WASI (zero) point one" or "WASI preview one". The official specification uses the "preview" moniker, however in most communication the form "WASI 0.1" is now preferred. This target triple was chosen because it not only maps to both terms, but also more closely resembles the target terminology used in other programming languages. This is something the WASI Preview 2 specification also makes note of.

Timeline

This table provides the dates and cut-offs for the target rename fromwasm32-wasi to wasm32-wasip1. The dates in this table do not apply to the newly-introduced wasm32-wasi-preview1-threads target; this will be renamed towasm32-wasip1-threads in Rust 1.78 without going through a transition period. The tier 3 wasm32-wasip2 target will also be made available in Rust 1.78.

date

Rust Stable

Rust Beta

Rust Nightly

Notes

2024-02-08

1.76

1.77

1.78

wasm32-wasip1 available on nightly

2024-03-21

1.77

1.78

1.79

wasm32-wasip1 available on beta

2024-05-02

1.78

1.79

1.80

wasm32-wasip1 available on stable

2024-06-13

1.79

1.80

1.81

warn if wasm32-wasi is used on nightly

2024-07-25

1.80

1.81

1.82

warn if wasm32-wasi is used on beta

2024-09-05

1.81

1.82

1.83

warn if wasm32-wasi is used on stable

2024-10-17

1.82

1.83

1.84

wasm32-wasi unavailable on nightly

2024-11-28

1.83

1.84

1.85

wasm32-wasi unavailable on beta

2025-01-09

1.84

1.85

1.86

wasm32-wasi unavailable on stable

Conclusion

In this post we've discussed the upcoming updates to Rust's WASI targets. Come Rust 1.78 the wasm32-wasip1 (tier 2) and wasm32-wasip2 (tier 3) targets will be added. In Rust 1.81 we will begin warning if wasm32-wasi is being used. And in Rust 1.84, the existing wasm32-wasi target will be removed. This will free up wasm32-wasi to eventually be used for a WASI 1.0 target. Users will have 8 months to switch to the new target name when they update their Rust toolchains.

The wasm32-wasip2 target marks the start of native support for WASI 0.2. In order to target it today from Rust, people are encouraged to usecargo-component tool instead. The plan is to eventually graduate wasm32-wasip2 to a tier-2 target, at which point cargo-component will be upgraded to support it natively instead.

With WASI 0.2 finally stable, it's an exciting time for WebAssembly development. We're happy for Rust to begin implementing native support for WASI 0.2, and we're excited for what this will enable people to build.

Continue Reading…

Rust Blog

Changes to `u128`/`i128` layout in 1.77 and 1.78

Rust has long had an inconsistency with C regarding the alignment of 128-bit integers on the x86-32 and x86-64 architectures. This problem has recently been resolved, but the fix comes with some effects that are worth being aware of.

As a user, you most likely do not need to worry about these changes unless you are:

  1. Assuming the alignment of i128/u128 rather than using align_of
  2. Ignoring the improper_ctypes* lints and using these types in FFI

There are also no changes to architectures other than x86-32 and x86-64. If your code makes heavy use of 128-bit integers, you may notice runtime performance increases at a possible cost of additional memory use.

This post documents what the problem was, what changed to fix it, and what to expect with the changes. If you are already familiar with the problem and only looking for a compatibility matrix, jump to the Compatibility section.

Background

Data types have two intrinsic values that relate to how they can be arranged in memory; size and alignment. A type's size is the amount of space it takes up in memory, and its alignment specifies which addresses it is allowed to be placed at.

The size of simple types like primitives is usually unambiguous, being the exact size of the data they represent with no padding (unused space). For example, an i64 always has a size of 64 bits or 8 bytes.

Alignment, however, can vary. An 8-byte integer could be stored at any memory address (1-byte aligned), but most 64-bit computers will get the best performance if it is instead stored at a multiple of 8 (8-byte aligned). So, like in other languages, primitives in Rust have this most efficient alignment by default. The effects of this can be seen when creating composite types (playground link):

use core::mem::{align_of, offset_of};

#[repr(C)]
struct Foo {
    a: u8,  // 1-byte aligned
    b: u16, // 2-byte aligned
}

#[repr(C)]
struct Bar {
    a: u8,  // 1-byte aligned
    b: u64, // 8-byte aligned
}

println!("Offset of b (u16) in Foo: {}", offset_of!(Foo, b));
println!("Alignment of Foo: {}", align_of::<Foo>());
println!("Offset of b (u64) in Bar: {}", offset_of!(Bar, b));
println!("Alignment of Bar: {}", align_of::<Bar>());

Output:

Offset of b (u16) in Foo: 2
Alignment of Foo: 2
Offset of b (u64) in Bar: 8
Alignment of Bar: 8

We see that within a struct, a type will always be placed such that its offset is a multiple of its alignment - even if this means unused space (Rust minimizes this by default when repr(C) is not used).

These numbers are not arbitrary; the application binary interface (ABI) says what they should be. In the x86-64 psABI (processor-specific ABI) for System V (Unix & Linux),Figure 3.1: Scalar Types tells us exactly how primitives should be represented:

C type

Rust equivalent

sizeof

Alignment (bytes)

char

i8

1

1

unsigned char

u8

1

1

short

i16

2

2

unsigned short

u16

2

2

long

i64

8

8

unsigned long

u64

8

8

The ABI only specifies C types, but Rust follows the same definitions both for compatibility and for the performance benefits.

The Incorrect Alignment Problem

If two implementations disagree on the alignment of a data type, they cannot reliably share data containing that type. Rust had inconsistent alignment for 128-bit types:

println!("alignment of i128: {}", align_of::<i128>());

// rustc 1.76.0
alignment of i128: 8

printf("alignment of __int128: %zu\n", _Alignof(__int128));

// gcc 13.2
alignment of __int128: 16

// clang 17.0.1
alignment of __int128: 16

(Godbolt link) Looking back at the psABI, we can see that Rust has the wrong alignment here:

C type

Rust equivalent

sizeof

Alignment (bytes)

__int128

i128

16

16

unsigned __int128

u128

16

16

It turns out this isn't because of something that Rust is actively doing incorrectly: layout of primitives comes from the LLVM codegen backend used by both Rust and Clang, among other languages, and it has the alignment for i128 hardcoded to 8 bytes.

Clang uses the correct alignment only because of a workaround, where the alignment is manually set to 16 bytes before handing the type to LLVM. This fixes the layout issue but has been the source of some other minor problems.2Rust does no such manual adjustement, hence the issue reported athttps://github.com/rust-lang/rust/issues/54341.

The Calling Convention Problem

There is an additional problem: LLVM does not always do the correct thing when passing 128-bit integers as function arguments. This was a known issue in LLVM, before itsrelevance to Rust was discovered.

When calling a function, the arguments get passed in registers (special storage locations within the CPU) until there are no more slots, then they get "spilled" to the stack (the program's memory). The ABI tells us what to do here as well, in the section 3.2.3 Parameter Passing:

Arguments of type __int128 offer the same operations as INTEGERs, yet they do not fit into one general purpose register but require two registers. For classification purposes __int128 is treated as if it were implemented as:

typedef struct {
   long low, high;
} __int128;

with the exception that arguments of type __int128 that are stored in memory must be aligned on a 16-byte boundary.

We can try this out by implementing the calling convention manually. In the below C example, inline assembly is used to call foo(0xaf, val, val, val) with val as0x0x11223344556677889900aabbccddeeff.

x86-64 uses the registers rdi, rsi, rdx, rcx, r8, and r9 to pass function arguments, in that order (you guessed it, this is also in the ABI). Each register fits a word (64 bits), and anything that doesn't fit gets pushed to the stack.

/* full example at <https://godbolt.org/z/5c8cb5cxs> */

/* to see the issue, we need a padding value to "mess up" argument alignment */
void foo(char pad, __int128 a, __int128 b, __int128 c) {
    printf("%#x\n", pad & 0xff);
    print_i128(a);
    print_i128(b);
    print_i128(c);
}

int main() {
    asm(
        /* load arguments that fit in registers */
        "movl    $0xaf, %edi \n\t"                /* 1st slot (edi): padding char (`edi` is the
                                                   * same as `rdi`, just a smaller access size) */
        "movq    $0x9900aabbccddeeff, %rsi \n\t"  /* 2rd slot (rsi): lower half of `a` */
        "movq    $0x1122334455667788, %rdx \n\t"  /* 3nd slot (rdx): upper half of `a` */
        "movq    $0x9900aabbccddeeff, %rcx \n\t"  /* 4th slot (rcx): lower half of `b` */
        "movq    $0x1122334455667788, %r8  \n\t"  /* 5th slot (r8):  upper half of `b` */
        "movq    $0xdeadbeef4c0ffee0, %r9  \n\t"  /* 6th slot (r9):  should be unused, but
                                                   * let's trick clang! */

        /* reuse our stored registers to load the stack */
        "pushq   %rdx \n\t"                       /* upper half of `c` gets passed on the stack */
        "pushq   %rsi \n\t"                       /* lower half of `c` gets passed on the stack */

        "call    foo \n\t"                        /* call the function */
        "addq    $16, %rsp \n\t"                  /* reset the stack */
    );
}

Running the above with GCC prints the following expected output:

0xaf
0x11223344556677889900aabbccddeeff
0x11223344556677889900aabbccddeeff
0x11223344556677889900aabbccddeeff

But running with Clang 17 prints:

0xaf
0x11223344556677889900aabbccddeeff
0x11223344556677889900aabbccddeeff
0x9900aabbccddeeffdeadbeef4c0ffee0
//^^^^^^^^^^^^^^^^ this should be the lower half
//                ^^^^^^^^^^^^^^^^ look familiar?

Surprise!

This illustrates the second problem: LLVM expects an i128 to be passed half in a register and half on the stack when possible, but this is not allowed by the ABI.

Since the behavior comes from LLVM and has no reasonable workaround, this is a problem in both Clang and Rust.

Solutions

Getting these problems resolved was a lengthy effort by many people, starting with a patch by compiler team member Simonas Kazlauskas in 2017: D28990. Unfortunately, this wound up reverted. It was later attempted again in D86310 by LLVM contributor Harald van Dijk, which is the version that finally landed in October 2023.

Around the same time, Nikita Popov fixed the calling convention issue with D158169. Both of these changes made it into LLVM 18, meaning all relevant ABI issues will be resolved in both Clang and Rust that use this version (Clang 18 and Rust 1.78 when using the bundled LLVM).

However, rustc can also use the version of LLVM installed on the system rather than a bundled version, which may be older. To mitigate the chance of problems from differing alignment with the same rustc version, a proposal was introduced to manually correct the alignment like Clang has been doing. This was implemented by Matthew Maurer in #11672.

Since these changes, Rust now produces the correct alignment:

println!("alignment of i128: {}", align_of::<i128>());

// rustc 1.77.0
alignment of i128: 16

As mentioned above, part of the reason for an ABI to specify the alignment of a datatype is because it is more efficient on that architecture. We actually got to see that firsthand: the initial performance run with the manual alignment change showed nontrivial improvements to compiler performance (which relies heavily on 128-bit integers to work with integer literals). The downside of increasing alignment is that composite types do not always fit together as nicely in memory, leading to an increase in usage. Unfortunately this meant some of the performance wins needed to be sacrificed to avoid an increased memory footprint.

Compatibility

The most imporant question is how compatibility changed as a result of these fixes. In short, i128 and u128 with Rust using LLVM 18 (the default version starting with 1.78) will be completely compatible with any version of GCC, as well as Clang 18 and above (released March 2024). All other combinations have some incompatible cases, which are summarized in the table below:

Compiler 1

Compiler 2

status

Rust ≥ 1.78 with bundled LLVM (18)

GCC (any version)

Fully compatible

Rust ≥ 1.78 with bundled LLVM (18)

Clang ≥ 18

Fully compatible

Rust ≥ 1.77 with LLVM ≥ 18

GCC (any version)

Fully compatible

Rust ≥ 1.77 with LLVM ≥ 18

Clang ≥ 18

Fully compatible

Rust ≥ 1.77 with LLVM ≥ 18

Clang < 18

Storage compatible, has calling bug

Rust ≥ 1.77 with LLVM < 18

GCC (any version)

Storage compatible, has calling bug

Rust ≥ 1.77 with LLVM < 18

Clang (any version)

Storage compatible, has calling bug

Rust < 1.773

GCC (any version)

Incompatible

Rust < 1.773

Clang (any version)

Incompatible

GCC (any version)

Clang ≥ 18

Fully compatible

GCC (any version)

Clang < 18

Storage compatible with calling bug

Effects & Future Steps

As mentioned in the introduction, most users will notice no effects of this change unless you are already doing something questionable with these types.

Starting with Rust 1.77, it will be reasonably safe to start experimenting with 128-bit integers in FFI, with some more certainty coming with the LLVM update in 1.78. There is ongoing discussion about lifting the lint in an upcoming version, but we want to be cautious and avoid introducing silent breakage for users whose Rust compiler may be built with an older LLVM.

  1. https://bugs.llvm.org/show_bug.cgi?id=50198
  2. https://github.com/llvm/llvm-project/issues/20283
  3. Rust < 1.77 with LLVM 18 will have some degree of compatibility, this is just an uncommon combination. ↩2

Continue Reading…

Rust Blog

Announcing Rust 1.77.1

The Rust team has published a new point release of Rust, 1.77.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.77.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.77.1

Cargo enabled stripping of debuginfo in release builds by defaultin Rust 1.77.0. However, due to a pre-existing issue, debuginfo stripping does not behave in the expected way on Windows with the MSVC toolchain.

Rust 1.77.1 therefore disables the new Cargo behavior on Windows for targets that use MSVC. There are no changes for other targets. We plan to eventually re-enable debuginfo stripping in release mode in a later Rust release.

Contributors to 1.77.1

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

Continue Reading…

Rust Blog

Announcing Rust 1.77.0

The Rust team is happy to announce a new version of Rust, 1.77.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.77.0 with:

$ rustup update stable

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

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.77.0 stable

This release is relatively minor, but as always, even incremental improvements lead to a greater whole. A few of those changes are highlighted in this post, and others may yet fill more niche needs.

C-string literals

Rust now supports C-string literals (c"abc") which expand to a nul-byte terminated string in memory of type &'static CStr. This makes it easier to write code interoperating with foreign language interfaces which require nul-terminated strings, with all of the relevant error checking (e.g., lack of interior nul byte) performed at compile time.

Support for recursion in async fn

Async functions previously could not call themselves due to a compiler limitation. In 1.77, that limitation has been lifted, so recursive calls are permitted so long as they use some form of indirection to avoid an infinite size for the state of the function.

This means that code like this now works:

async fn fib(n: u32) -> u32 {
   match n {
       0 | 1 => 1,
       _ => Box::pin(fib(n-1)).await + Box::pin(fib(n-2)).await
   }
}

offset_of!

1.77.0 stabilizes offset_of! for struct fields, which provides access to the byte offset of the relevant public field of a struct. This macro is most useful when the offset of a field is required without an existing instance of a type. Implementing such a macro is already possible on stable, but without an instance of the type the implementation would require tricky unsafe code which makes it easy to accidentally introduce undefined behavior.

Users can now access the offset of a public field with offset_of!(StructName, field). This expands to a usize expression with the offset in bytes from the start of the struct.

Enable strip in release profiles by default

Cargo profileswhich do not enable debuginfo in outputs (e.g., debug = 0) will enable strip = "debuginfo" by default.

This is primarily needed because the (precompiled) standard library ships with debuginfo, which means that statically linked results would include the debuginfo from the standard library even if the local compilations didn't explicitly request debuginfo.

Users which do want debuginfo can explicitly enable it with thedebugflag in the relevant Cargo profile.

Clippy adds a new incompatible_msrv lint

The Rust project only supports the latest stable release of Rust. Some libraries aim to have an older minimum supported Rust version (MSRV), typically verifying this support by compiling in CI with an older release. However, when developing new code, it's convenient to use latest documentation and the latest toolchain with fixed bugs, performance improvements, and other improvements. This can make it easy to accidentally start using an API that's only available on newer versions of Rust.

Clippy has added a new lint, incompatible_msrv, which will inform users if functionality being referenced is only available on newer versions than theirdeclared MSRV.

Stabilized APIs

Other changes

Check out everything that changed in Rust, Cargo, and Clippy.

Contributors to 1.77.0

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

Continue Reading…

Rust Blog

Announcing Rustup 1.27.0

The rustup team is happy to announce the release of rustup version 1.27.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.27.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.27.0

This long-awaited Rustup release has gathered all the new features and fixes since April 2023. These changes include improvements in Rustup's maintainability, user experience, compatibility and documentation quality.

Also, it's worth mentioning that Dirkjan Ochtman (djc) and rami3l (rami3l) have joined the team and are coordinating this new release.

At the same time, we have granted Daniel Silverstone (kinnison) and 二手掉包工程师 (hi-rustin) their well-deserved alumni status in this release cycle. Kudos for your contributions over the years and your continuous guidance on maintaining the project!

The headlines for this release are:

  1. Basic support for the fish shell has been added. If you're using fish, PATH configs for your Rustup installation will be added automatically from now on.
    Please note that this will only take effect on installation, so if you have already installed Rustup on your machine, you will need to reinstall it. For example, if you have installed Rustup via rustup.rs, simply follow rustup.rs's instructions again; if you have installed Rustup using some other method, you might want to reinstall it using that same method.
  2. Rustup support for loongarch64-unknown-linux-gnu as a host platform has been added. This means you should be able to install Rustup via rustup.rs and no longer have to rely on loongnix.cn or self-compiled installations.
    Please note that as of March 2024, loongarch64-unknown-linux-gnu is a "tier 2 platform with host tools", so Rustup is guaranteed to build for this platform. According to Rust's target tier policy, this does not imply that these builds are also guaranteed to work, but they often work to quite a good degree and patches are always welcome!

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.27.0 possible!

  • Anthony Perkins (acperkins)
  • Tianqi (airstone42)
  • Alex Gaynor (alex)
  • Alex Hudspith (alexhudspith)
  • Alan Somers (asomers)
  • Brett (brettearle)
  • Burak Emir (burakemir)
  • Chris Denton (ChrisDenton)
  • cui fliter (cuishuang)
  • Dirkjan Ochtman (djc)
  • Dezhi Wu (dzvon)
  • Eric Swanson (ericswanson-dfinity)
  • Prikshit Gautam (gautamprikshit1)
  • hev (heiher)
  • 二手掉包工程师 (hi-rustin)
  • Kamila Borowska (KamilaBorowska)
  • klensy (klensy)
  • Jakub Beránek (Kobzol)
  • Kornel (kornelski)
  • Matt Harding (majaha)
  • Mathias Brossard (mbrossard)
  • Christian Thackston (nan60)
  • Ruohui Wang (noirgif)
  • Olivier Lemasle (olivierlemasle)
  • Chih Wang (ongchi)
  • Pavel Roskin (proski)
  • rami3l (rami3l)
  • Robert Collins (rbtcollins)
  • Sandesh Pyakurel (Sandesh-Pyakurel)
  • Waffle Maybe (WaffleLapkin)
  • Jubilee (workingjubilee)
  • WÁNG Xuěruì (xen0n)
  • Yerkebulan Tulibergenov (yerke)
  • Renovate Bot (renovate)

Continue Reading…

Rust Blog

crates.io: Download changes

Like the rest of the Rust community, crates.io has been growing rapidly, with download and package counts increasing 2-3x year-on-year. This growth doesn't come without problems, and we have made some changes to download handling on crates.io to ensure we can keep providing crates for a long time to come.

The Problem

This growth has brought with it some challenges. The most significant of these is that all download requests currently go through the crates.io API, occasionally causing scaling issues. If the API is down or slow, it affects all download requests too. In fact, the number one cause of waking up our crates.io on-call team is "slow downloads" due to the API having performance issues.

Additionally, this setup is also problematic for users outside of North America, where download requests are slow due to the distance to the crates.io API servers.

The Solution

To address these issues, over the last year we have decided to make some changes:

Starting from 2024-03-12, cargo will begin to download crates directly from our static.crates.io CDN servers.

This change will be facilitated by modifying the config.json file on the package index. In other words: no changes to cargo or your own system are needed for the changes to take effect. The config.json file is used by cargo to determine the download URLs for crates, and we will update it to point directly to the CDN servers, instead of the crates.io API.

Over the past few months, we have made several changes to the crates.io backend to enable this:

  • We announced the deprecation of "non-canonical" downloads, which would be harder to support when downloading directly from the CDN.
  • We changed how downloads are counted. Previously, downloads were counted directly on the crates.io API servers. Now, we analyze the log files from the CDN servers to count the download requests.

crates.io download graph of an arbitrary crate showing that on 2024-02-16, download numbers increased

The latter change has caused the download numbers of most crates to increase, as some download requests were not counted before. Specifically, crates.io mirrors were often downloading directly from the CDN servers already, and those downloads had previously not been counted. For crates with a lot of downloads these changes will be barely noticeable, but for smaller crates, the download numbers have increased quite a bit over the past few weeks since we enabled this change.

Expected Outcomes

We expect these changes to significantly improve the reliability and speed of downloads, as the performance of the crates.io API servers will no longer affect the download requests. Over the next few weeks, we will monitor the performance of the system to ensure that the changes have the expected effects.

We have noticed that some non-cargo build systems are not using the config.json file of the index to build the download URLs. We will reach out to the maintainers of those build systems to ensure that they are aware of the change and to help them update their systems to use the new download URLs. The old download URLs will continue to work, but these systems will be missing out on the potential performance improvement.

We are excited about these changes and believe they will greatly improve the reliability of crates.io. We look forward to hearing your feedback!

Continue Reading…

Rust Blog

Clippy: Deprecating `feature = "cargo-clippy"`

Since Clippy v0.0.97 and before it was shipped with rustup, Clippy implicitly added a feature = "cargo-clippy" config1 when linting your code with cargo clippy.

Back in the day (2016) this was necessary to allow, warn or deny Clippy lints using attributes:

#[cfg_attr(feature = "cargo-clippy", allow(clippy_lint_name))]

Doing this hasn't been necessary for a long time. Today, Clippy users will set lint levels with tool lint attributes using the clippy:: prefix:

#[allow(clippy::lint_name)]

The implicit feature = "cargo-clippy" has only been kept for backwards compatibility, but will now be deprecated.

Alternative

As there is a rare use case for conditional compilation depending on Clippy, we will provide an alternative. So in the future you will be able to use:

#[cfg(clippy)]

Transitioning

Should you have instances of feature = "cargo-clippy" in your code base, you will see a warning from the new Clippy lintclippy::deprecated_clippy_cfg_attr. This lint can automatically fix your code. So if you should see this lint triggering, just run:

cargo clippy --fix -- -Aclippy::all -Wclippy::deprecated_clippy_cfg_attr

This will fix all instances in your code.

In addition, check your .cargo/config file for:

[target.'cfg(feature = "cargo-clippy")']
rustflags = ["-Aclippy::..."]

If you have this config, you will have to update it yourself, by either changing it to cfg(clippy) or taking this opportunity to transition to setting lint levels in Cargo.toml directly.

Motivation for Deprecation

Currently, there's a call for testing, in order to stabilize checking conditional compilation at compile time, aka cargo check -Zcheck-cfg. If we were to keep the feature = "cargo-clippy" config, users would start seeing a lot of warnings on their feature = "cargo-clippy"conditions. To work around this, they would either need to allow the lint or have to add a dummy feature to their Cargo.toml in order to silence those warnings:

[features]
cargo-clippy = []

We didn't think this would be user friendly, and decided that instead we want to deprecate the implicit feature = "cargo-clippy" config and replace it with theclippy config.

  1. It's likely that you didn't even know that Clippy implicitly sets this config (which was not a Cargo feature). This is intentional, as we stopped advertising and documenting this a long time ago.

Continue Reading…

Rust Blog

Updated baseline standards for Windows targets

The minimum requirements for Tier 1 toolchains targeting Windows will increase with the 1.78 release (scheduled for May 02, 2024). Windows 10 will now be the minimum supported version for the *-pc-windows-* targets. These requirements apply both to the Rust toolchain itself and to binaries produced by Rust.

Two new targets have been added with Windows 7 as their baseline: x86_64-win7-windows-msvc and i686-win7-windows-msvc. They are starting as Tier 3 targets, meaning that the Rust codebase has support for them but we don't build or test them automatically. Once these targets reach Tier 2 status, they will be available to use via rustup.

Affected targets

  • x86_64-pc-windows-msvc
  • i686-pc-windows-msvc
  • x86_64-pc-windows-gnu
  • i686-pc-windows-gnu
  • x86_64-pc-windows-gnullvm
  • i686-pc-windows-gnullvm

Why are the requirements being changed?

Prior to now, Rust had Tier 1 support for Windows 7, 8, and 8.1 but these targets no longer meet our requirements. In particular, these targets could no longer be tested in CI which is required by the Target Tier Policy and are not supported by their vendor.

Continue Reading…

Rust Blog

Rust participates in Google Summer of Code 2024

We're writing this blog post to announce that the Rust Project will be participating in Google Summer of Code (GSoC) 2024. If you're not eligible or interested in participating in GSoC, then most of this post likely isn't relevant to you; if you are, this should contain some useful information and links.

Google Summer of Code (GSoC) is an annual global program organized by Google that aims to bring new contributors to the world of open-source. The program pairs organizations (such as the Rust Project) with contributors (usually students), with the goal of helping the participants make meaningful open-source contributions under the guidance of experienced mentors.

As of today, the organizations that have been accepted into the program have been announced by Google. The GSoC applicants now have several weeks to send project proposals to organizations that appeal to them. If their project proposal is accepted, they will embark on a 12-week journey during which they will try to complete their proposed project under the guidance of an assigned mentor.

We have prepared a list of project ideas that can serve as inspiration for potential GSoC contributors that would like to send a project proposal to the Rust organization. However, applicants can also come up with their own project ideas. You can discuss project ideas or try to find mentors in the #gsoc Zulip stream. We have also prepared a proposal guide that should help you with preparing your project proposals.

You can start discussing the project ideas with Rust Project maintainers immediately. The project proposal application period starts on March 18, 2024, and ends on April 2, 2024 at 18:00 UTC. Take note of that deadline, as there will be no extensions!

If you are interested in contributing to the Rust Project, we encourage you to check out our project idea list and send us a GSoC project proposal! Of course, you are also free to discuss these projects and/or try to move them forward even if you do not intend to (or cannot) participate in GSoC. We welcome all contributors to Rust, as there is always enough work to do.

This is the first time that the Rust Project is participating in GSoC, so we are quite excited about it. We hope that participants in the program can improve their skills, but also would love for this to bring new contributors to the Project and increase the awareness of Rust in general. We will publish another blog post later this year with more information about our participation in the program.

Continue Reading…

Rust Blog

2023 Annual Rust Survey Results

Hello, Rustaceans!

The Rust Survey Team is excited to share the results of our 2023 survey on the Rust Programming language, conducted between December 18, 2023 and January 15, 2024. As in previous years, the 2023 State of Rust Survey was focused on gathering insights and feedback from Rust users, and all those who are interested in the future of Rust more generally.

This eighth edition of the survey surfaced new insights and learning opportunities straight from the global Rust language community, which we will summarize below. In addition to this blog post, this year we have also prepared a report containing charts with aggregated results of all questions in the survey. Based on feedback from recent years, we have also tried to provide more comprehensive and interactive charts in this summary blog post. Let us know what you think!

Our sincerest thanks to every community member who took the time to express their opinions and experiences with Rust over the past year. Your participation will help us make Rust better for everyone.

There's a lot of data to go through, so strap in and enjoy!

Participation

Survey

Started

Completed

Completion rate

Views

2022

11 482

9 433

81.3%

25 581

2023

11 950

9 710

82.2%

16 028

As shown above, in 2023, we have received 37% fewer survey views in vs 2022, but saw a slight uptick in starts and completions. There are many reasons why this could have been the case, but it’s possible that because we released the 2022 analysis blog so late last year, the survey was fresh in many Rustaceans’ minds. This might have prompted fewer people to feel the need to open the most recent survey. Therefore, we find it doubly impressive that there were more starts and completions in 2023, despite the lower overall view count.

Community

This year, we have relied on automated translations of the survey, and we have asked volunteers to review them. We thank the hardworking volunteers who reviewed these automated survey translations, ultimately allowing us to offer the survey in seven languages: English, Simplified Chinese, French, German, Japanese, Russian, and Spanish. We decided not to publish the survey in languages without a translation review volunteer, meaning we could not issue the survey in Portuguese, Ukrainian, Traditional Chinese, or Korean.

The Rust Survey team understands that there were some issues with several of these translated versions, and we apologize for any difficulty this has caused. We are always looking for ways to improve going forward and are in the process of discussing improvements to this part of the survey creation process for next year.

We saw a 3pp increase in respondents taking this year’s survey in English – 80% in 2023 and 77% in 2022. Across all other languages, we saw only minor variations – all of which are likely due to us offering fewer languages overall this year due to having fewer volunteers.

Rust user respondents were asked which country they live in. The top 10 countries represented were, in order: United States (22%), Germany (12%), China (6%), United Kingdom (6%), France (6%), Canada (3%), Russia (3%), Netherlands (3%), Japan (3%), and Poland (3%) . We were interested to see a small reduction in participants taking the survey in the United States in 2023 (down 3pp from the 2022 edition) which is a positive indication of the growing global nature of our community! You can try to find your country in the chart below:

[PNG] [SVG]

Once again, the majority of our respondents reported being most comfortable communicating on technical topics in English at 92.7% — a slight difference from 93% in 2022. Again, Chinese was the second-highest choice for preferred language for technical communication at 6.1% (7% in 2022).

[PNG] [SVG]

We also asked whether respondents consider themselves members of a marginalized community. Out of those who answered, 76% selected no, 14% selected yes, and 10% preferred not to say.

We have asked the group that selected “yes” which specific groups they identified as being a member of. The majority of those who consider themselves a member of an underrepresented or marginalized group in technology identify as lesbian, gay, bisexual, or otherwise non-heterosexual. The second most selected option was neurodivergent at 41% followed by trans at 31.4%. Going forward, it will be important for us to track these figures over time to learn how our community changes and to identify the gaps we need to fill.

[PNG] [SVG]

As Rust continues to grow, we must acknowledge the diversity, equity, and inclusivity (DEI)-related gaps that exist in the Rust community. Sadly, Rust is not unique in this regard. For instance, only 20% of 2023 respondents to this representation question consider themselves a member of a racial or ethnic minority and only 26% identify as a woman. We would like to see more equitable figures in these and other categories. In 2023, the Rust Foundation formed a diversity, equity, and inclusion subcommittee on its Board of Directors whose members are aware of these results and are actively discussing ways that the Foundation might be able to better support underrepresented groups in Rust and help make our ecosystem more globally inclusive. One of the central goals of the Rust Foundation board's subcommittee is to analyze information about our community to find out what gaps exist, so this information is a helpful place to start. This topic deserves much more depth than is possible here, but readers can expect more on the subject in the future.

Rust usage

In 2023, we saw a slight jump in the number of respondents that self-identify as a Rust user, from 91% in 2022 to 93% in 2023.

[PNG] [SVG]

Of those who used Rust in 2023, 49% did so on a daily (or nearly daily) basis — a small increase of 2pp from the previous year.

[PNG] [SVG]

31% of those who did not identify as Rust users cited the perception of difficulty as the primary reason for not having used it, with 67% reporting that they simply haven’t had the chance to prioritize learning Rust yet, which was once again the most common reason.

[PNG] [SVG] [Wordcloud of open answers]

Of the former Rust users who participated in the 2023 survey, 46% cited factors outside their control (a decrease of 1pp from 2022), 31% stopped using Rust due to preferring another language (an increase of 9pp from 2022), and 24% cited difficulty as the primary reason for giving up (a decrease of 6pp from 2022).

[PNG] [SVG] [Wordcloud of open answers]

Rust expertise has generally increased amongst our respondents over the past year! 23% can write (only) simple programs in Rust (a decrease of 6pp from 2022), 28% can write production-ready code (an increase of 1pp), and 47% consider themselves productive using Rust — up from 42% in 2022. While the survey is just one tool to measure the changes in Rust expertise overall, these numbers are heartening as they represent knowledge growth for many Rustaceans returning to the survey year over year.

[PNG] [SVG]

In terms of operating systems used by Rustaceans, the situation is very similar to the results from 2022, with Linux being the most popular choice of Rust users, followed by macOS and Windows, which have a very similar share of usage.

[PNG] [SVG] [Wordcloud of open answers]

Rust programmers target a diverse set of platforms with their Rust programs, even though the most popular target by far is still a Linux machine. We can see a slight uptick in users targeting WebAssembly, embedded and mobile platforms, which speaks to the versatility of Rust.

[PNG] [SVG] [Wordcloud of open answers]

We cannot of course forget the favourite topic of many programmers: which IDE (developer environment) do they use. Visual Studio Code still seems to be the most popular option, with RustRover (which was released last year) also gaining some traction.

[PNG] [SVG] [Wordcloud of open answers]

You can also take a look at the linked wordcloud that summarizes open answers to this question (the "Other" category), to see what other editors are also popular.

Rust at Work

We were excited to see a continued upward year-over-year trend of Rust usage at work. 34% of 2023 survey respondents use Rust in the majority of their coding at work — an increase of 5pp from 2022. Of this group, 39% work for organizations that make non-trivial use of Rust.

[PNG] [SVG]

Once again, the top reason employers of our survey respondents invested in Rust was the ability to build relatively correct and bug-free software at 86% — a 4pp increase from 2022 responses. The second most popular reason was Rust’s performance characteristics at 83%.

[PNG] [SVG]

We were also pleased to see an increase in the number of people who reported that Rust helped their company achieve its goals at 79% — an increase of 7pp from 2022. 77% of respondents reported that their organization is likely to use Rust again in the future — an increase of 3pp from the previous year. Interestingly, we saw a decrease in the number of people who reported that using Rust has been challenging for their organization to use: 34% in 2023 and 39% in 2022. We also saw an increase of respondents reporting that Rust has been worth the cost of adoption: 64% in 2023 and 60% in 2022.

[PNG] [SVG]

There are many factors playing into this, but the growing awareness around Rust has likely resulted in the proliferation of resources, allowing new teams using Rust to be better supported.

In terms of technology domains, it seems that Rust is especially popular for creating server backends, web and networking services and cloud technologies.

[PNG] [SVG] [Wordcloud of open answers]

You can scroll the chart to the right to see more domains. Note that the Database implementation and Computer Games domains were not offered as closed answers in the 2022 survey (they were merely submitted as open answers), which explains the large jump.

It is exciting to see the continued growth of professional Rust usage and the confidence so many users feel in its performance, control, security and safety, enjoyability, and more!

Challenges

As always, one of the main goals of the State of Rust survey is to shed light on challenges, concerns, and priorities on Rustaceans’ minds over the past year.

Of those respondents who shared their main worries for the future of Rust (9,374), the majority were concerned about Rust becoming too complex at 43% — a 5pp increase from 2022. 42% of respondents were concerned about a low level of Rust usage in the tech industry. 32% of respondents in 2023 were most concerned about Rust developers and maintainers not being properly supported — a 6pp increase from 2022.

We saw a notable decrease in respondents who were not at all concerned about the future of Rust, 18% in 2023 and 30% in 2022.

Thank you to all participants for your candid feedback which will go a long way toward improving Rust for everyone.

[PNG] [SVG] [Wordcloud of open answers]

Closed answers marked with N/A were not present in the previous (2022) version of the survey.

In terms of features that Rust users want to be implemented, stabilized or improved, the most desired improvements are in the areas of traits (trait aliases, associated type defaults, etc.), const execution (generic const expressions, const trait methods, etc.) and async (async closures, coroutines).

[PNG] [SVG] [Wordcloud of open answers]

It is interesting that 20% of respondents answered that they wish Rust to slow down the development of new features, which likely goes hand in hand with the previously mentioned worry that Rust becomes too complex.

The areas of Rust that Rustaceans seem to struggle with the most seem to be asynchronous Rust, the traits and generics system and also the borrow checker.

[PNG] [SVG] [Wordcloud of open answers]

Respondents of the survey want the Rust maintainers to mainly prioritize fixing compiler bugs (68%), improving the runtime performance of Rust programs (57%) and also improving compile times (45%).

[PNG] [SVG]

Same as in recent years, respondents noted that compilation time is one of the most important areas that should be improved. However, it is interesting to note that respondents also seem to consider runtime performance to be more important than compile times.

Looking ahead

Each year, the results of the State of Rust survey help reveal the areas that need improvement in many areas across the Rust Project and ecosystem, as well as the aspects that are working well for our community.

We are aware that the survey has contained some confusing questions, and we will try to improve upon that in the next year's survey. If you have any suggestions for the Rust Annual survey, please let us know!

We are immensely grateful to those who participated in the 2023 State of Rust Survey and facilitated its creation. While there are always challenges associated with developing and maintaining a programming language, this year we were pleased to see a high level of survey participation and candid feedback that will truly help us make Rust work better for everyone.

If you’d like to dig into more details, we recommend you to browse through the full survey report.

Continue Reading…