Using stability attributes in user crates

I suppose user crates can use #[deprecated] including since and reasons attributes (like:

#[deprecated(since = "0.1.8", reasons = "Use bar() instead")]

). Can #[stable] and #[unstable] be used as well? What should be used for feature in that case? Or is it not necessary?

I'd like to use deprecations. It feels much better against the users, even especially when the crate is no where near stable; it's nice to be able to signal changes up front.

I would use #[unstable] and #[deprecated].

I remember this being discussed previously, and some core team member saying that stability was supposed to be private to rustc, basically.

However I agree, I think that this system could work really well in the Rust ecosystem.

Right now these attributes are no-ops outside of the standard library, but we've left the option open to reuse these attributes in a design that is user-oriented. The intent is to come up with a new design that is not tied so closely to Rust's release channels.

In the meantime there is soon going to be a deprecation period that ends with making these attributes errors on beta until such time as we have a new design: https://github.com/rust-lang/rust/issues/22830.

2 Likes

I am thinking perhaps they could work something like this:

  • #[deprecated] would produce warning in release and error in test configuration. The idea is that if a crate uses deprecated items of it's dependency, it should still compile for it's users, but it should fail tests to make it impossible to miss during testing, at least by default (#[allow(deprecated)] would of course still make it compile).

  • #[unstable] would be opt-out from the semantic versioning. In crates with non-zero major version it would mean that given item may be changed incompatibly without major version change. #[unstable(feature = "foo")] would imply #[cfg(feature = "foo")]. It would be nice if something checked that the feature is not default.

  • For #[stable] I don't see much use beyond documentation, since in crates with major version 0 nothing is considered stable and in crates with non-zero major version everything public is by default.

The standard library uses #[stable] to opt-in to semantic versioning, which I think is the better default: it's not possible to accidentally stabilise something, as everything that is stable needs its own #[stable] attribute. This incurs an annotation burden, but I've never found it to be a problem in the standard library.

The system used in std:

  • inherits #[unstable], so #[unstable] pub mod foo { pub fn bar() {} } considers foo::bar unstable
  • doesn't inherit #[stable], so the same example with stable would consider foo::bar has "unmarked"
  • outlaws unmarked items: everything needs to either be annotated or be inside an unstable parent.

(deprecated is orthogonal to those attributes: an item can be deprecated & stable or deprecated & unstable.)

It's not clear to me if a system like this will be a good fit for external libraries, but I think it has worked pretty well in the standard library so far.