This is probably my pet peeve with the docs. Maybe it made sense when Rust was less mature, but now? Or even five years ago? Why would I ever want to see unstable features in stable docs? At the very least the styling should be very different, graying the unstable items out instead of displaying a bright warning note.
Because when you're searching for functionality you want, it's important to distinguish between "that doesn't exist" (maybe you should suggest adding it) and "that exists but it isn't stable yet" (maybe you could try it out on nightly, see if it does what you want, see why it isn't stabilized yet, help get it stabilized).
It's already a challenge to get people to test nightly features, but at least when they're visible in searches people are more likely to know about them and try them out.
For some time I thought that there needs to be something in between nightly and stable. Maybe on beta you could have semi-stable features. Where the feature can change, but there will be a deprecation period a few releases.
If the feature is going away entirely, a longer deprecating period (ideally this should be for things that won't go away, that we know we want for sure, just not the exact shape).
The exact scheme can be debated and worked out, but I think something along these lines would be useful.
The risk of a rug pull is why I don't use nightly (except for debugging and tracing features that my program doesn't depend on, such as sanitizers, miri, etc). (I'm also willing to use nightly rustfmt, because that is the only way to get sane formatting of imports. And even if there is a rugpull my code will still build.)
As a rust user nightly might as well not exist for the most part.
Thinking some more about this: why am I not okay with nightly rust features but am okay with libraries that have semver breaking versions?
I'm not sure I know exactly myself. But there are a number of contributing factors:
I can update at my own pace. And it is not all or nothing as most libraries are smaller and more narrow in scope than std.
Complete rug pulls are rare. Features are usually not removed without replacement. (Notable counterexample is that Tokio has a cfg for unstable features. I don't use that except for debugging either.)
Good libraries provide migration guides. When nightly breaks this is extremely rare.
Libraries with breaking semversions are extremely disruptive if they’re deep in the dependency graph, because then you get split ecosystems and have to write adapters, embed two copies, etc, until everyone has caught up. std is a very deep dependency.
Many Rust users use nightly, simply because some useful things sit on nightly forever. Some people might develop on nightly but ship builds wit stable, due to some debugging features (here is one from 2021, assert_matches!). Due to rust-toolchain.toml, there is no confusion on whether some project should be built with nightly, and if yes from which date.
Indeed seeing some useful nightly API might make someone to switch to nightly, which is generally a good thing for the ecosystem (the more people on nightly, the more unstable features will get tested)
So my usual reaction on seeing something useful but unstable in stable docs isn't "I should use nightly to use that" but "I will wait for this to be stabilised" – which is the opposite of what you want, because it means that not only do you get less testing of the unstable feature, you also get less visibility into potential alternatives that might be used to make it work in stable (and even less use of Rust in general – if I'm waiting for a feature to be stabilised, it encourages me to spend more of my time on other unrelated projects, that might be written in a different language).
For some context, I don't use recent Rust compilers – I use whatever comes with my Linux distro. Likewise, I don't depend on Rust crates unless those are packaged by the Linux distro (and in general try to avoid them as much as possible even if they are packaged). This is largely because I'm unable to compile Rust from source (both because it needs a recent Rust version – which I don't have – and because I don't think my computer is powerful enough to actually do the compile), and because I'm unwilling to download binaries using rustup or a similar tool.
As such, if the aim is to get people like me to experiment with unstable features and give feedback on them, they would have to be made available in the stable compiler (presumably behind a command-line option that allows opting into unstable and possibly broken features). My interpretation of the restriction that currently prevents this on stable is that, in effect, you actively want to discourage people from using unstable features by making them significantly harder to get at (presumably with the purpose of discouraging people distributing code that uses unstable features or people assuming that the features in question are less prone to breaking than they actually are).
IIRC there actually is such a compiler flag at the moment, but designed to be very hidden and highly discouraged to use? As such, I've never really looked into using it, but if "get people testing unstable features" is more important than "discourage people from using unstable features on stable", it might be worth looking at.
(I guess one possible viewpoint is "recently added unstable features need more testing than older unstable features", and of course recently added features wouldn't be available in an older stable compiler even if using them were allowed, so it would be of no use for testing them.)
I realize packaged Rust compilers are all-around a contentious topic, but I think it also means you’re not the target audience for providing feedback, because the version of the unstable API you’re spending time with isn’t necessarily the current one, and even if it is you won’t have feedback for the next iteration for quite a while. That isn’t related to the compiler version having gone through the stabilization process or not; you wouldn’t be the target audience even if your distro packaged a nightly compiler with all the features available.
Yes, I understand that such feedback might be too out-of-date to be useful.
I get the impression that feedback on unstable features is primarily useful to help drive design decisions, e.g. to choose between two possible versions of an API – and that feedback is obtainable even by testing an old version (e.g. even if the project decides to change from version A of an API to version B, feedback of the form of "version A works for me" or "version A doesn't work for me" is useful in that it shapes the requirements on B). For example, I've given feedback on a version of an unstable feature that was intentionally rejected, to help inform the decision about whether the rejection was correct or not and what the deails should be. (In this case, not having a nightly compiler wasn't an obstacle because the existing nightly compilers would have rejected the code anyway, so any reasoning would have to be done theoretically rather than with practical code, or using workarounds to make it work on current nightly but those would also make it work on current stable.)
It would, of course, not be very helpful for catching bugs or other situations which were implementation issues rather than design issues.
If the compiler removes the unstable feature or makes a backward incompatible change, you won't be able to update the compiler without breaking your code. In contrast, for a library, you can accommodate by anchoring on an old version of a library forever while automatically updating the compiler.
Rephrasing this one more time in a way I think is rather enlightening:
std has such stringent compatibility guarantees because you don't control your std dependency.
For any other semver specified dependency, you control what version of it you get, and when (even if) you upgrade. For std, you get whatever std version is provided by the compiler. And the expectation is that you should upgrade your compiler version eagerly. rustup-toolchain.toml can specify a specific toolchain for a project, but that's the exception rather than the norm; a project is just expected to build with whatever the most recent compiler is, especially so if it's actively maintained. You don't "get to" stay on an old compiler toolchain like you do an old version of a dependency.
I generally think this is the better side of things, compared to projects locked to C++11 out of vague compatibility concerns.
Something like a "#![feature(tomorrow)]" that enables a bunch of "safe" std features might make it easier for binary projects to target "latest nightly" without needing to themselves evaluate what features are "safe" to experiment with. I'd put the cutoff at "doesn't enable new functionality" (could be implemented using only stable APIs), personally.
+1 on having something in-between, I can't use the entire breadth of unstable features, but I could happily use something if it came with a promise of "this functionality works and will always exist in some form, but we might backwards-incompatibly change the API to access this in future versions."
I wouldn't even need a lengthy deprecation period, since CI is set to deny all warnings (including deprecation warnings) anyways. Just something in the release notes saying "hey, if you called Foo::bar(), we moved that to Foo::baz() now" would be enough.
Regarding an in-between, I've mused the idea of having multiple tiers of stability:
internal: never intended to be used by downstream users; primarily accessible for macros if public
experimental: similar to an incomplete feature; major/breaking changes are expected, and the feature may be removed entirely. This would require an #[allow_internal_experimental] (current #[allow_internal_unstable])
preview: feature-complete, likely to be stabilized. Refinement is expected, but breaking changes are not (though they're still permitted). Can be used without an attribute at each use site; only the crate-level feature gate is necessary.
stable: status quo
My vision involves expanding stability to any crate, but that's certainly more involved.
It'd be nice to have a stable-compiler opt-in to features that are almost stable. There's a bunch of them stuck because people can't agree on the method name or exact types (e.g. whether to support Result only or wait for a Try trait).
But it would be even nicer if std supported semver versioning like other crates.