The Rust project is currently working towards a slate of 17 project goals, with 6 of them designated 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
"Beyond the `&`"
| Progress | |
| Point of contact | |
| Champions | compiler (Oliver Scherer), lang (TC) |
| Task owners |
2 detailed updates available.
Status update:
- [x] pattern matching support of
&pin const|mut Ttypes, merged. - [x]
&pinpattern andref pin mutbinding mode, merged. - [ ]
Drop::pin_drop, waiting for review (new updates since the last review).- Unresolved question: the current implementation requires changing the
src/docs/booksubmodule, but the top repo and the sub repo must be changed together to pass the CI tests in both repos. It's because a default body is added toDrop::dropand it becomes a provided method instead of a required method in rustdoc. Is there any way to avoid that? (Possibly keep rustdoc treatingDrop::dropas a required method?)
- Unresolved question: the current implementation requires changing the
- [ ] coercion between
&pin const|mut Tand&{mut} T, waiting for review (fresh).
Status update:
Regarding the TODO list in the next 6 months, here is the current status:
Introduce &pin mut|const place borrowing syntax
- [x] parsing: #135731(https://github.com/rust-lang/rust/pull/135731), merged.
- [ ] lowering and borrowck: not started yet.
I've got some primitive ideas about borrowck, and I probably need to confirm with someone who is familiar with MIR/borrowck before starting to implement.
A pinned borrow consists two MIR statements:
- a borrow statement that creates the mutable reference,
- and an adt aggregate statement that put the mutable reference into the
Pinstruct.
I may have to add a new borrow kind so that pinned borrows can be recognized. Then traverse the dataflow graph to make sure that pinned places cannot been moved.
Pattern matching of &pin mut|const T types
In the past few months, I have struggled with the !Unpin stuffs (the original design sketch Alternative A), trying implementing it, refactoring, discussing on zulips, and was constantly confused; luckily, we have finally reached a new agreement of the Alternative B version.
- [ ] #139751(https://github.com/rust-lang/rust/pull/139751) under review (reimplemented regarding Alternative B).
Support drop(&pin mut self) for structually pinned types
- [ ] adding a new
Drop::pin_drop(&pin mut self)method: draft PR #144537(https://github.com/rust-lang/rust/pull/144537)
Supporting both Drop::drop(&mut self) and Drop::drop(&pin mut self) seems to introduce method-overloading to Rust, which I think might need some more general ways to handle (maybe by a rustc attribute?). So instead, I'd like to implemenent this via a new method Drop::pin_drop(&pin mut self) first.
Introduce &pin pat pattern syntax
Not started yet (I'd prefer doing that when pattern matching of &pin mut|const T types is ready).
Support &pin mut|const T -> &|&mut T coercion (requires T: Unpin of &pin mut T -> &mut T)
Not started yet. (It's quite independent, probably someone else can help with it)
Support auto borrowing of &pin mut|const place in method calls with &pin mut|const self receivers
Seems to be handled by Autoreborrow traits?
| Progress | |
| Point of contact | |
| Champions | |
| Task owners |
2 detailed updates available.
We've worked towards coherence checking of the CoerceShared trait, and have come to a conclusion that (at least as a first step) only one lifetime, the first one, shall participate in reborrowing. Problems abound with how to store the field mappings for CoerceShared.
Initial implementation of a Reborrow trait for types with only lifetimes with exclusive reference semantics is working but not yet upstreamed not in review. CoerceShared implementation is not yet started.
Proper composable implementation will likely require a different tactic than the current one. Safety and validity checks are currently absent as well and will require more work.
"Flexible, fast(er) compilation"
| Progress | |
| Point of contact | |
| Champions | cargo (Eric Huss), compiler (David Wood), libs (Amanieu d'Antras) |
| Task owners |
2 detailed updates available.
We've now opened our first batch of RFCs: rust-lang/rfcs#3873, rust-lang/rfcs#3874 and rust-lang/rfcs#3875
Recently we've been working on feedback on the multi-staged format of the RFC. We've also shared the RFC outside of our sync call group to people from a variety of project teams and potential users too.
We're now receiving feedback that is much more detail-oriented, as opposed to being about the direction and scope of the RFC, which is a good indication that the overall strategy for shipping this RFC seems promising. We're continuing to address feedback to ensure the RFC is clear, consistent and technically feasible. David's feeling is that we've probably got another couple rounds of feedback from currently involved people and then we'll invite more people from various groups before publishing parts of the RFC formally.
| Progress | |
| Point of contact | |
| Champions | |
| Task owners | bjorn3, Folkert de Vries, [Trifecta Tech Foundation] |
No detailed updates available.
| Progress | |
| Point of contact | |
| Champions | |
| Task owners |
No detailed updates available.
"Higher-level Rust"
| Progress | |
| Point of contact | |
| Champions | |
| Task owners |
5 detailed updates available.
New blog post:
- https://smallcultfollowing.com/babysteps/blog/2025/11/10/just-call-clone/
Exploring one way to make things more ergonomic while remaining explicit, which is to make .clone() and .alias() (1) understood by move closure desugaring and (2) optimized away when redundant.
Three new blog posts:
- Explicit capture clauses
- Bikeshedding
Handleand other follow-up thoughts - But then again...maybe alias?
The most important conclusions from those posts are
- Explicit capture clauses would be useful, I proposed one specific syntax but bikeshedding will be required. To be "ergonomic" we need the ability to refer to full places, e.g.,
move(cx.foo.clone()) || use(cx.foo). - We should consider
AliasorShareas the name forHandletrait; I am currently leaning towardsAliasbecause it can be used as both a noun and a verb and is a bit more comparable to clone -- i.e., you can say "analiasoffoo" just like you'd say "acloneoffoo". - We should look for solutions that apply well to
cloneandaliasso that higher-level Rust gets the ergonomic benefits even when cloning "heavier-weight" types to whichAliasdoes not apply.
Update:
There has been more discussion about the Handle trait on Zulip and elsewhere. Some of the notable comments:
- Downsides of the current name: it's a noun, which doesn't follow Rust naming convention, and the verb
handleis very generic and could mean many things. - Alternative names proposed:
Entangle/entangleorentangled,Share/share,Alias/alias, orRetain/retain. if we want to seriously hardcore on the science names --Mitose/mitoseorFission/fission. - There has been some criticism pointing out that focusing on handles means that other types which might be "cheaply cloneable" don't qualify.
For now I will go on using the term Handle, but I agree with the critique that it should be a verb, and currently prefer Alias/alias as an alternative.
I'm continuing to work my way through the backlog of blog posts about the conversations from Rustconf. The purposes of these blog posts is not just to socialize the ideas more broadly but also to help myself think through them. Here is the latest post:
https://smallcultfollowing.com/babysteps/blog/2025/10/13/ergonomic-explicit-handles/
The point of this post is to argue that, whatever else we do, Rust should have a way to create handles/clones (and closures that work with them) which is at once explicit and ergonomic.
To give a preview of my current thinking, I am working now on the next post which will discuss how we should add an explicit capture clause syntax. This is somewhat orthogonal but not really, in that an explicit syntax would make closures that clone more ergonomic (but only mildly). I don't have a proposal I fully like for this syntax though and there are a lot of interesting questions to work out. As a strawperson, though, you might imagine [this older proposal I wrote up](https://hackmd.io/Niko Matsakis/SyI0eMFXO?type=view), which would mean something like this:
#![allow(unused)] fn main() { let actor1 = async move(reply_tx.handle()) { reply_tx.send(...); }; let actor2 = async move(reply_tx.handle()) { reply_tx.send(...); }; }
This is an improvement on
#![allow(unused)] fn main() { let actor1 = { let reply_tx = reply_tx.handle(); async move(reply_tx.handle()) { reply_tx.send(...); } }; }
but only mildly.
The next post I intend to write would be a variant on "use, use everywhere" that recommends method call syntax and permitting the compiler to elide handle/clone calls, so that the example becomes
#![allow(unused)] fn main() { let actor1 = async move { reply_tx.handle().send(...); // -------- due to optimizations, this would capture the handle creation to happen only when future is *created* }; }
This would mean that cloning of strings and things might benefit from the same behavior:
#![allow(unused)] fn main() { let actor1 = async move { reply_tx.handle().send(some_id.clone()); // -------- the `some_id.clone()` would occur at future creation time }; }
The rationable that got me here is (a) minimizing perceived complexity and focusing on muscle memory (just add .clone() or .handle() to fix use-after-move errors, no matter when/where they occur). The cost of course is that (a) Handle/Clone become very special; and (b) it blurs the lines on when code execution occurs. Despite the .handle() occurring inside the future (resp. closure) body, it actually executes when the future (resp. closure) is created in this case (in other cases, such as a closure that implements Fn or FnMut and hence executes more than once, it might occur during each execution as well).
I wrote up a brief summary of my current thoughts on Zulip; I plan to move this content into a series of blog posts, but I figured it was worth laying it out here too for those watching this space:
09:11 (1) I don't think clones/handles are categorically different when it comes to how much you want to see them made explicit; some applications want them both to be explicit, some want them automatic, some will want a mix -- and possibly other kinds of categorizations.
09:11 (2) But I do think that if you are making everything explicit, it's useful to see the difference between a general purpose clone and a handle.
09:12 (3) I also think there are many classes of software where there is value in having everything explicit -- and that those classes are often the ones most in Rust's "sweet spot". So we should make sure that it's possible to have everything be explicit ergonomically.
09:12 (4) This does not imply that we can't make automatic clones/handles possible too -- it is just that we should treat both use cases (explicit and automatic) as first-class in importance.
09:13 (5) Right now I'm focused on the explicit case. I think this is what the use-use-everywhere was about, though I prefer a different proposal now -- basically just making handle and clone methods understood and specially handled by the compiler for optimization and desugaring purposes. There are pros and cons to that, obviously, and that's what I plan to write-up in more detail.
09:14 (6) On a related note, I think we also need explicit closure captures, which is a whole interesting design space. I don't personally find it "sufficient" for the "fully explicit" case but I could understand why others might think it is, and it's probably a good step to take.
09:15 (7) I go back and forth on profiles -- basically a fancy name for lint-groups based on application domain -- and whether I think we should go that direction, but I think that if we were going to go automatic, that's the way I would do it: i.e., the compiler will automatically insert calls to clone and handle, but it will lint when it does so; the lint can by deny-by-default at first but applications could opt into allow for either or both.
I previously wanted allow-by-default but I've decided this is a silly hill to die on, and it's probably better to move in smaller increments.
I posted this blog post that proposes that we ought to name the trait Handle and define it as a trait where clone produces an "entangled" value -- i.e., a second handle to the same underlying value.
Before that, there's been a LOT of conversation that hasn't made its way onto this tracking issue. Trying to fix that! Here is a brief summary, in any case:
- It began with the first Rust Project Goals program in 2024H2, where [@jkelleyrtp][] from Dioxus wrote a thoughtful blog post about a path to high-level Rust that eventually became a 2024H2 project goal towards ergonomic ref-counting.
- I wrote a series of blog posts about a trait I called
Claim. - Josh Triplett and I talked and Josh Triplett opened RFC #3680[], which proposed a
usekeyword anduse ||closures. Reception, I would say, was mixed; yes, this is tackling a real problem, but there were lots of concerns on the approach. I summarized the key points here. - Santiago Pastorino implemented experimental support for (a variant of) RFC #3680[] as part of the 2025H1 project goal.
- I authored a 2025H2 project goal proposing that we create an alternative RFC focused on higher-level use-cases which prompted Josh Triplett and I have to have a long and fruitful conversation in which he convinced me that this was not the right approach.
- We had a lang-team design meeting on 2025-08-27 in which I presented this survey and summary of the work done thus far.
- And then at the RustConf 2025 Unconf we had a big group discussion on the topic that I found very fruitful, as well as various follow-up conversations with smaller groups. The name
Handlearose from this and I plan to be posting further thoughts as a result.
RFC #3680: https://github.com/rust-lang/rfcs/pull/3680
Goals looking for help
Other goal updates
| Progress | |
| Point of contact | |
| Champions | compiler (Oliver Scherer), lang (Tyler Mandry), libs (David Tolnay) |
| Task owners |
No detailed updates available.
| Progress | |
| Point of contact | |
| Champions | |
| Task owners |
No detailed updates available.
| Progress | |
| Point of contact | |
| Champions | |
| Task owners |
2 detailed updates available.
Here's our first status update!
-
We've been experimenting with a few different ways of emitting retags in codegen, as well as a few different forms that retags should take at this level. We think we've settled on a set of changes that's worth sending out to the community for feedback, likely as a pre-RFC. You can expect more engagement from us on this level in the next couple of weeks.
-
We've used these changes to create an initial working prototype for BorrowSanitizer that supports finding Tree Borrows violations in tiny, single-threaded Rust programs. We're working on getting Miri's test suite ported over to confirm that everything is working correctly and that we've quashed any false positives or false negatives.
-
This coming Monday, I'll be presenting on BorrowSanitizer and this project goal at the Workshop on Supporting Memory Safety in LLVM. Please reach out if you're attending and would like to chat more in person!
| Progress | |
| Point of contact | |
| Champions | compiler (Manuel Drehwald), lang (TC) |
| Task owners | Manuel Drehwald, LLVM offload/GPU contributors |
2 detailed updates available.
Automatic Differentiation
Time for the next update. By now, we've had std::autodiff for around a year in upstream rustc, but not in nightly. In order to get some more test users, I asked the infra team to re-evaluate just shipping autodiff as-is. This means that for the moment, we will increase the binary size of rustc by ~5%, even for nightly users who don't use this feature. We still have an open issue to avoid this overhead by using dlopen, please reach out if you have time to help. Thankfully, my request was accepted, so I spent most of my time lately preparing that release.
- As part of my cleanup I went through old issues, and realized we now partly support rlib's! That's a huge improvement, because it means you can use autodiff not only in your
main.rsfile, but also in dependencies (eitherlib.rs, or even rely on crates that use autodiff). With the help of Ben Kimock I figured out how to get the remaining cases covered, hopefully the PR will land soon. - I started documentation improvements in https://github.com/rust-lang/rust/pull/149082 and https://github.com/rust-lang/rust/pull/148201, which should be visible on the website from tomorrow onwards. They are likely still not perfect, so please keep opening issues if you have questions.
- We now provide a helpful error message if a user forgets enabling
lto=fat: https://github.com/rust-lang/rust/pull/148855 - After two months of work, [@sgasho][] managed to add Rust CI to enzyme! Unfortunately, Enzyme devs broke and disabled it directly, so we'll need to talk about maintaining it as part of shipping Enzyme in nightly.
I have the following elements on my TODO list as part shipping AD on nightly
- Re-enable macOS build (probably easy)
- Talk with Enzyme Devs about maintenance
- Merge rlib support (under review)
- upstream ADbenchmarks from r-l/enzyme to r-l/r as codegen tests (easy)
- Write a block post/article for https://blog.rust-lang.org/inside-rust/
GPU offload
- The llvm dev talk about GPU programming went great, I got to talk to a lot of other developers in the area of llvm offload. I hope to use some of the gained knowledge soon. Concrete steps planned are the integration of libc-gpu for IO from kernels, as well as moving over my code from the OpenMP API to the slightly lower level liboffload API.
- We confirmed that our gpu offload prototype works on more hardware. By now we have the latest AMD APU generation covered, as well as an MI 250X and an RTX 4050. My own Laptop with a slightly older
AMD Ryzen 7 PRO 7840Uunfortunately turned out to be not supported by AMD drivers. - The offload intrinsic PR by Marcelo Domínguez is now marked as ready, and I left my second round of review. Hopefully, we can land it soon!
- I spend some time trying to build and potentially ship the needed offload changes in nightly, unfortunately I still fail to build it in CI: https://github.com/rust-lang/rust/pull/148671.
All in all, I think we made great progress over the last month, and it's motivating that we finally have no blockers left for flipping the llvm.enzyme config on our nightly builds.
A longer update of the changes over the fall. We had two gsoc contributors and a lot of smaller improvements for std::autodiff. The first two improvements were already mentioned as draft PRs in the previous update, but got merged since. I also upstreamed more std::offload changes.
- Marcelo Domínguez refactored the autodiff frontend to be a proper rustc intrinsic, rather than just hackend into the frontend like I first implemented it. This already solved multiple open issues, reduced the code size, and made it generally easier to maintain going forward.
- Karan Janthe upstreamed a first implementation of "TypeTrees", which lowers rust type and layout information to Enzyme, our autodiff backend. This makes it more likely that you won't see compilation failures with the error message "Can not deduce type of ". We might refine in the future what information exactly we lower.
- Karan Janthe made sure that std::autodiff has support for f16 and and f128 types.
- One more of my offload PRs landed. I also figured out why the LLVM-IR generated by the std::offload code needed some manual adjustments in the past. We were inconsistent when communicating with LLVM's offload module, about whether we'd want a magic, extra, dyn_ptr argument, that enables kernels to use some extra features. We don't use these features yet, but for consistency we now always generate and expect the extra pointer. The bugfix is currently under review, once it lands upstream, rustc is able to run code on GPUs (still with a little help of clang).
- Marcelo Domínguez refactored my offload frontend, again introducing a proper rustc intrinsic. That code will still need to go through review, but once it lands it will get us a lot closer to a usable frontend. He also started to generate type information for our offload backend to know how many bytes to copy to and from the devices. This is a very simplified version of our autodiff typetrees.
- At RustChinaConf, I was lucky to run into the wild linker author David Lattimore, which helped me to create a draft PR that can dlopen Enzyme at runtime. This means we could ship it via rustup for people interested in std::autodiff, and don't have to link it in at build time, which would increase binary size even for those users that are not interested in it. There are some open issues, so please reach out if you have time to get the PR ready!
- [@sgasho][] spend a lot of time trying to get Rust into the Enzyme CI. Unfortunately that is a tricky process due to Enzyme's CI requirements, so it's not merged yet.
- I tried to simplify building std::autodiff by marking it as compatible with download-llvm-ci. Building LLVM from source was previously the by far slowest part of building rustc with autodiff, so this has a large potential. Unfortunately the CI experiments revealed some issues around this setting. We think we know why Enzyme's Cmake causes issues here and are working on a fix to make it more reliable.
- [@osamakader][] and bjorn3 looked into automatically enabling fat-lto when autodiff is enabled. In the past, forgetting to enable fat-lto resulted in incorrect (zero) derivatives. The first approach unfortunately wasn't able to cover all cases, so we need to see whether we can handle it nicely. If that turns out to be too complicated, we will revert it and instead "just" provide a nice error message, rather than returning incorrect derivatives.
All-in-all I spend a lot more time on infra (dlopen, cmake, download-llvm-ci, ...) then I'd like, but on the happy side there are only so many features left that I want to support here so there is an end in sight. I am also about to give a tech-talk at the upcoming LLVM dev meeting about safe GPU programming in Rust.
| Progress | |
| Point of contact | |
| Champions | |
| Task owners | (depending on the flag) |
4 detailed updates available.
Update from the 2025-11-05 meeting.
-Zharden-sls / rust#136597
Wesley Wiser left a comment on the PR, Andr
-Zno-jump-tables / rust#145974
Merged, expected to ship in Rust 1.93. The Linux kernel added support for the new name for the option (-Cjump-tables=n).
-Cunsigned-char
We've discussed adding an option analogous to -funsigned-char in GCC and Clang, that would allow you to set whether std::ffi::c_char is represented by i8 or u8. Right now, this is platform-specific and should map onto whatever char is in C on the same platform. However, Linux explicitly sets char to be unsigned and then our Rust code conflicts with that. And isn this case the sign is significant.
Rust for Linux works around this this with their rust::ffi module, but now that they've switched to the standard library's CStr type, they're running into it again with the as_ptr method.
Tyler mentioned https://docs.rs/ffi_11/latest/ffi_11/ which preserves the char / signed char / unsigned char distinction.
Grouping target modifier flags
The proposed unsigned-char option is essentially a target modifier. We have several more of these (e.g. llvm-args, no-redzone) in the Rust compiler and Josh suggested we distinguish them somehow. E.g. by giving them the same prefix or possibly creating a new config option (right now we have -C and -Z, maybe we could add -T for target modifiers) so they're distinct from the e.g. the codegen options.
Josh started a Zulip thread here: https://rust-lang.zulipchat.com/#narrow/channel/131828-t-compiler/topic/Grouping.20target.20modifier.20options.3F/with/546524232
#![register_tool] / rust#66079 / RFC#3808
Tyler looked at the RFC. The Crubit team started using register_tool but then moved to using an attribute instead. He proposed we could do something similar here, although it would require a new feature and RFC.
The team was open to seeing how it would work.
- [merged] Sanitizers target modificators / https://github.com/rust-lang/rust/pull/138736
- [merged] Add assembly test for -Zreg-struct-return option / https://github.com/rust-lang/rust/pull/145382
- [merged] CI: rfl: move job forward to Linux v6.17-rc5 to remove temporary commits / https://github.com/rust-lang/rust/pull/146368
-Zharden-sls/ https://github.com/rust-lang/rust/pull/136597- Waiting on review
#![register_tool]/ https://github.com/rust-lang/rust/issues/66079- Waiting on https://github.com/rust-lang/rfcs/pull/3808
-Zno-jump-tables/ https://github.com/rust-lang/rust/pull/145974- Active FCP, waiting on 2 check boxes
I've updated the top-level description to show everything we're tracking here (please let me know if anything's missing or incorrect!).
| Progress | |
| Point of contact | |
| Champions | cargo (Ed Page), compiler (b-naber), crates-io (Carol Nichols) |
| Task owners |
No detailed updates available.
| Progress | |
| Point of contact | |
| Champions | compiler (Oliver Scherer), lang (Scott McMurray), libs (Josh Triplett) |
| Task owners | oli-obk |
2 detailed updates available.
Another related PR:
https://github.com/rust-lang/rust/pull/148820
I implemented an initial MVP supporting only tuples and primitives (tho those are just opaque things you can't interact with further), and getting offsets for the tuple fields as well as the size of the tuple: https://github.com/rust-lang/rust/pull/146923
There are two designs of how to expose this from a libs perspective, but after a sync meeting with scottmcm yesterday we came to the conclusion that neither is objectively better at this stage so we're just going to go with the nice end-user UX version for now. For details see the PR description.
Once the MVP lands, I will mentor various interested contributors who will keep adding fields to the Type struct and variants the TypeKind enum.
The next major step is restricting what information you can get from structs outside of the current module or crate. We want to honor visibility, so an initial step would be to just never show private fields, but we want to explore allowing private fields to be shown either just within the current module or via some opt-in marker trait
| Progress | |
| Point of contact | |
| Champions | |
| Task owners |
1 detailed update available.
This project goal has been completed. I updated the first issue to reflect it. Closing the issue then.
| Progress | |
| Point of contact | |
| Champions | |
| Task owners |
3 detailed updates available.
The new system has been running in production without any major issues for a few weeks now. In a few weeks, I plan to start using the second collector, and then announce the new system to Project members to tell them how they can use its new features.
We moved forward with the implementation, and the new job queue system is now being tested in production on a single test pull request. Most things seem to be working, but there are a few things to iron out and some profiling to be done. I expect that within a few weeks we could be ready to switch to the new system fully in production.
It is possible to now run the system with two different machines on two different architectures however there is work to be done to make this more robust.
We have worked on ironing out the last bits and pieces for dequeuing benchmarks as well as creating a new user interface to reflect multiple collectors doing work. Presently work is mostly on polishing the UI and handing edge cases through manual testing.
Queue Work:
- https://github.com/rust-lang/rustc-perf/pull/2212
- https://github.com/rust-lang/rustc-perf/pull/2214
- https://github.com/rust-lang/rustc-perf/pull/2216
- https://github.com/rust-lang/rustc-perf/pull/2221
- https://github.com/rust-lang/rustc-perf/pull/2226
- https://github.com/rust-lang/rustc-perf/pull/2230
- https://github.com/rust-lang/rustc-perf/pull/2231
Ui:
- https://github.com/rust-lang/rustc-perf/pull/2217
- https://github.com/rust-lang/rustc-perf/pull/2220
- https://github.com/rust-lang/rustc-perf/pull/2224
- https://github.com/rust-lang/rustc-perf/pull/2227
- https://github.com/rust-lang/rustc-perf/pull/2232
- https://github.com/rust-lang/rustc-perf/pull/2233
- https://github.com/rust-lang/rustc-perf/pull/2236
| Progress | |
| Point of contact | |
| Champions | compiler (David Wood), lang (Niko Matsakis), libs (Amanieu d'Antras) |
| Task owners |
2 detailed updates available.
Notes from our meeting today:
Syntax proposal: only keyword
We are exploring the use of a new only keyword to identify "special" bounds that will affect the default bounds applied to the type parameter. Under this proposal, T: SizeOfVal is a regular bound, but T: only SizeOfVal indicates that the T: const Sized default is suppressed.
For the initial proposal, only can only be applied to a known set of traits; one possible extension would be to permit traits with only supertraits to also have only applied to them:
#![allow(unused)] fn main() { trait MyDeref: only SizeOfVal { } fn foo<T: only MyDeref>() { }// equivalent to
trait MyDeref: only SizeOfVal { } fn foo<T: MyDeref + only SizeOfVal>() { } }
We discussed a few other syntactic options:
- A
^SizeOfValsigil was appealing due to the semver analogy but rejected on the basis of it being cryptic and hard to google. - The idea of applying the keyword to the type parameter
only T: SizeOfValsort of made sense, but it would not compose well if we add additional families of "opt-out" traits likeDestructandForget, and it's not clear how it applies to supertraits.
Transitioning target
After testing, we confirmed that relaxing Target bound will result in significant breakage without some kind of transitionary measures.
We discussed the options for addressing this. One option would be to leverage "Implementable trait aliases" RFC but that would require a new trait (Deref20XX) that has a weaker bound an alias trait Deref = Deref20XX<Target: only SizeOfVal>. That seems very disruptive.
Instead, we are considering an edition-based approach where (in Rust 2024) a T: Target bound is defaulted to T: Deref<Target: only SizeOfVal> and (in Rust 20XX) T: Target is defaulted to T: Deref<Target: only Pointee>. The edition transition would therefore convert bounds to one of those two forms to be fully explicit.
One caveat here is that this edition transition, if implemented naively, would result in stronger bounds than are needed much of the time. Therefore, we will explore the option of using bottom-up analysis to determine when transitioning whether the 20XX bound can be used instead of the more conservative 2024 bound.
Supertrait bounds
We explored the implications of weakening supertrait bounds a bit, looking at this example
and#![allow(unused)] fn main() { trait FooTr<T: ?Sized> {}struct Foo<T: ?Sized>(std::marker::PhantomData<T>);
fn bar<T: ?Sized>() {}
trait Bar: FooTr<Self> /*: no longer MetaSized */ { // ^^^^^^^^^^^ error! // real examples are
Pin }TypeOf::of: fn foo(&self, x: Foo<Self>) { // ^^^^^^^^^^^^ error! bar::<Self>(); // ^^^^^^^^^^ error!// real examples are in core::fmt and core::iter: trait DoThing { fn do_thing() {} } impl<T: ?Sized> DoThing for T { default fn do_thing() {} } impl<T: Sized> DoThing for T { fn do_thing() {} } self.do_thing(); // ^^^^^^^^^^^^^ error! // specialisation case is not an issue because that feature isn't stable, we can adjust core, but is a hazard with expanding trait hierarchies in future if stabilisation is ever stabilised }}
The experimental_default_bounds work originally added Self: Trait bounds to default methods but moved away from that because it could cause region errors (source 1 / source 2). We expect the same would apply to us but we are not sure.
We decided not to do much on this, the focus remains on the Deref::Target transition as it has more uncertainty.
Sized hierarchy
The focus right now is on the "non-const" parts of the proposal, as the "const" parts are blocked on the new trait solver (https://github.com/rust-lang/rust-project-goals/issues/113). Now that the types team FCP https://github.com/rust-lang/rust/pull/144064 has completed, work can proceed to land the implementation PRs. David Wood plans to split the RFC to separate out the "non-const" parts of the proposal so it can move independently, which will enable extern types.
To that end, there are three interesting T-lang design questions to be considered.
Naming of the traits
The RFC currently proposes the following names
SizedMetaSizedPointeeSized
However, these names do not follow the "best practice" of naming the trait after the capability that it provides. As champion Niko is recommending we shift to the following names:
Sized-- should righly be calledSizeOf, but oh well, not worth changing.SizeOfVal-- named after the methodsize_of_valthat you get access to.Pointee-- the only thing you can do is point at it.
The last trait name is already used by the (unstable) std::ptr::Pointee trait. We do not want to have these literally be the same trait because that trait adds a Metadata associated type which would be backwards incompatible; if existing code uses T::Metadata to mean <T as SomeOtherTrait>::Metadata, it could introduce ambiguity if now T: Pointee due to defaults. My proposal is to rename std::ptr::Pointee to std::ptr::PointeeMetadata for now, since that trait is unstable and the design remains under some discussion. The two traits could either be merged eventually or remain separate.
Note that PointeeMetadata would be implemented automatically by the compiler for anything that implements Pointee.
Syntax opt-in
The RFC proposes that an explicit bound like T: MetaSized disabled the default T: Sized bound. However, this gives no signal that this trait bound is "special" or different than any other trait bound. Naming conventions can help here, signalling to users that these are special traits, but that leads to constraints on naming and may not scale as we consider using this mechanism to relax other defaults as proposed in my recent blog post. One idea is to use some form of syntax, so that T: MetaSized is just a regular bound, but (for example) T: =MetaSized indicates that this bound "disables" the default Sized bound. This gives users some signal that something special is going on. This = syntax is borrowing from semver constraints, although it's not a precise match (it does not mean that T: Sized doesn't hold, after all). Other proposals would be some other sigil (T: ?MetaSized, but it means "opt out from the traits above you"; T: #MetaSized, ...) or a keyword (no idea).
To help us get a feel for it, I'll use T: =Foo throughout this post.
Implicit trait supertrait bounds, edition interaction
In Rust 2024, a trait is implicitly ?Sized which gets mapped to =SizeOfVal:
#![allow(unused)] fn main() { trait Marker {} // cannot be implemented by extern types }
This is not desirable but changing it would be backwards incompatible if traits have default methods that take advantage of this bound:
#![allow(unused)] fn main() { trait NotQuiteMarker { fn dummy(&self) { let s = size_of_val(self); } } }
We need to decide how to handle this. Options are
- Just change it, breakage will be small (have to test that).
- Default to
=SizeOfValbut let users explicitly write=Pointeeif they want that. Bad because all traits will be incompatible with extern types. - Default to
=SizeOfValonly if defaulted methods are present. Bad because it's a backwards incompatible change to add a defaulted method now. - Default to
=Pointeebut addwhere Self: =SizeOfValimplicitly to defaulted methods. Now it's not backwards incompatible to add a new defaulted method, but it is backwards incompatible to change an existing method to have a default.
If we go with one of the latter options, Niko proposes that we should relax this in the next Edition (Rust 2026?) so that the default becomes Pointee (or maybe not even that, if we can).
Relaxing associated type bounds
Under the RFC, existing ?Sized bounds would be equivalent to =SizeOfVal. This is mostly fine but will cause problems in (at least) two specific cases: closure bounds and the Deref trait. For closures, we can adjust the bound since the associated type is unstable and due to the peculiarities of our Fn() -> T syntax. Failure to adjust the Deref bound in particular would prohibit the use of Rc<E> where E is an extern type, etc.
For deref bounds, David Wood is preparing a PR that simply changes the bound in a backwards incompatible way to assess breakage on crater. There is some chance the breakage will be small.
If the breakage proves problematic, or if we find other traits that need to be relaxed in a similar fashion, we do have the option of:
- In Rust 2024,
T: Derefbecomes equivalent toT: Deref<Target: SizeOfVal>unless written likeT: Deref<Target: =Pointee>. We add that annotation throughout stdlib. - In Rust 202X, we change the default, so that
T: Derefdoes not add any special bounds, and existing Rust 2024T: Derefis rewritten toT: Deref<Target: SizeOfVal>as needed.
Other notes
One topic that came up in discussion is that we may eventually wish to add a level "below" Pointee, perhaps Value, that signifies webassembly external values which cannot be pointed at. That is not currently under consideration but should be backwards compatible.
| Progress | |
| Point of contact | |
| Champions | |
| Task owners |