Pre-RFC: "Scheduled Removal" Parameter for "Deprecated" Attribute

Summary

Add a scheduled_removal paramter to the deprecated attribute, to allow library authors to specify when a deprecated item is scheduled to be removed.

Motivation

Currently, library authors can specify when an item is deprecated via the since parameter. However, when these deprecated items are removed, it is often confusing to some library users as to why a portion of the public API is removed, without noticing that it has been already deprecated. Users may not be aware that certain items have become deprecated, but even if they notice, they may not migrate to something else immediately. When the deprecated items do get removed, it is then difficult for users to migrate to another API. Removal of APIs can be sudden, and users are usually not prepared for sudden removal of deprecated items.

Having a scheduled_removal attribute, that specifies the version of which a deprecated item is removed would mean users obtain the information of when the deprecated item will be removed from the public API and hence can prepare or complete for the migration beforehand.

Guilde-level Explanation

This Pre-RFC proposes the addition of the scheduled_removal parameter to the deprecated attribute, as with the following example, when a version is specified:

#[deprecated(since = "0.2.1", scheduled_removal = "0.3.0")]
struct ThisItemIsDeprecated;

Usages of this struct would result in the following warning:

warning: use of deprecated unit struct `ThisItemIsDeprecated`
 --> src/main.rs:5:17
  |
5 |     let _ = ThisItemIsDeprecated;
  |             ^^^^^^^^^^^^^^^^^^^^
  |
  = note: this deprecated unit struct will be removed in version 0.3.0
  = note: `#[warn(deprecated)]` on by default

The added line note: this deprecated item will be removed in version 0.3.0 tells the user this deprecated item will be removed in version 0.3.0 and would make it clear that the user needs to migrate to another API before 0.3.0 lands, otherwise their code would break and fail to compile due to the removal of the API.

If no value is provided for the scheduled_removal parameter, like so:

#[deprecated(since = "0.2.1", scheduled_removal)]
struct ThisItemIsDeprecated;

Then theu sages of this struct would result in the following warning:

warning: use of deprecated unit struct `ThisItemIsDeprecated`
 --> src/main.rs:5:17
  |
5 |     let _ = ThisItemIsDeprecated;
  |             ^^^^^^^^^^^^^^^^^^^^
  |
  = note: this deprecated unit struct will be removed in a future version
  = note: `#[warn(deprecated)]` on by default

The second case is particularly useful - while knowing the version in which the item will be removed is a good thing for the library users, always requiring to put a version is troublesome, as putting a specific version then forces the maintainer to remove those items in that specific version.

Reference-level Explanation

The scheduled_removal parameter optionally takes in a &str value, signifying the version for which the deprecated item is scheduled to be removed.

The compiler takes this parameter into account when generating diagnostics for usages of an item marked as #[deprecated] using this parameter.

If a version is provided, the compiler will append a note line that reads: this deprecated {item_type} will be removed in version {version}, where {item type} is the type of item used, like a unit struct in this example, and the {version} being the version specified as the value to the scheduled_removal parameter. Otherwise, the appended note line would read like this deprecated {item_type} will be removed in a future version,

Drawbacks

None.

Rationale and alternatives

A separate scheduled_for_removal attribute

A separate attribute named scheduled_for_removal will be introduced. Usage of this attribute looks like, when a version is provided:

#[deprecated(since = "0.2.1")]
#[scheduled_for_removal(at = "0.3.0")]
struct ThisItemIsDeprecated;

Which would result in the following warning:

warning: use of unit struct `ThisItemIsDeprecated` that is scheduled for removal
 --> src/main.rs:5:17
  |
5 |     let _ = ThisItemIsDeprecated;
  |             ^^^^^^^^^^^^^^^^^^^^
  |
  = note: this unit struct will be removed in version 0.3.0
  = note: `#[warn(scheduled_for_removal)]` on by default

When a version is not specified:

#[deprecated(since = "0.2.1")]
#[scheduled_for_removal]
struct ThisItemIsDeprecated;

Which would result in the following warning:

warning: use of unit struct `ThisItemIsDeprecated` that is scheduled for removal
 --> src/main.rs:5:17
  |
5 |     let _ = ThisItemIsDeprecated;
  |             ^^^^^^^^^^^^^^^^^^^^
  |
  = note: `#[warn(scheduled_for_removal)]` on by default

The scheduled_for_removal lint would be introduced alongside with this attribute.

This was briefly considered but ultimately personally vetoed because another attribute is quite some work, and it is like an extension to the deprecated attribute - describing when will this item will be removed. Hence, adding the scheduled_removal parameter is preferred hence this proposal is based on a new parameter rather than a new attribute.

Prior art

  • The JetBrains Annotations Java package has a similar annotation: @ApiStatus.ScheduledForRemoval, which allows the library author to specify when an API will be removed from the public API entirely, and is mostly the inspiration for this Pre-RFC.
  • The @Deprecated attribute from the Java standard library, which has the boolean parameter forRemoval, it is used to specify whether the deprecated item will be removed in a future version - albeit a bit vague (as it does not allow you to specify which version would the item be removed).

Unresolved questions

  • How should this new attribute be named? (Albeit scheduled_removal sounds kind of weird to me, it is the currently the best name after discussion on Rust Internals).
  • How should the diagnostics be updated to accommodate for this? Currently, simply a note line is added. Is that good enough?

Future possibilities

Other API status attributes can be exposed to library authors, for example experimental to mark experimental APIs and emit corresponding warnings to the user about the usage of an unstable and experimental API of the library, that the code may break at any time without prior notice.

7 Likes

Just brainstorming:

  • migrate_before
  • resolve_before
  • replace_usage_before
2 Likes

Another one from Java is the @Deprecated attribute from the standard library, which has the boolean parameter forRemoval. It is set to true when the item will be removed in a future version.

2 Likes

Great catch, adding it to the Prior art section.

This sounds fine, especially when put into context as you showed:

#[deprecated(since = "0.2.1", scheduled_removal = "0.3.0")]
struct ThisItemIsDeprecated;

The suggestions by @mathstuf sound like they'd only make it confusing to understand.

2 Likes

Fair point... the last one, replace_usage_before is meh, but all the other seem to have to do with the code itself having issues and have to resolve those before a certain version, rather than related to deprecation.

Sometimes the best fit of the first thing is only discovered when the alternatives are worse :slight_smile: . (I have no attachment to any of my suggestions.) I also do not claim to know anything better than scheduled_removal either.

Indeed. It is probably best to keep the focus on the API being attributed rather than its consumers (as there may not be any anyways).

To add to the bikeshedding, since I mentioned it before, I propose forRemoval. Pretty direct and clear I think.

Also (not related with the bikeshedding, I'll use the already proposed scheduled_removal name in the following), what if a library mainteiner wants to deprecate for removal an item, but hasn't decided yet in which version the item will be removed?
I propose that using scheduled_removal without specifying a version means that the item will be removed in the future, but not in a specific version. Example:

#[deprecated(since = "0.2.1", scheduled_removal)]
struct ThisItemIsDeprecated;

While knowing the version in which the item will be removed is a good thing for the library users, I also think that always requiring to put a version is not good, since putting a specific version then forces the maintainer to remove those items in that specific version.

2 Likes

Indeed. As I said it is one of the drawbacks for library authors to always provide a version.

How about the scheduled_removal parameter optionally requires a value? As in, if no value is provided, then the compiler adds the note line like: this item is will be removed in a future version, and only when specifying a version would the compiler print the note line I wrote in the Pre-RFC.

(The Pre-RFC has been revised to reflect this change)

1 Like

That would be perfect I think.

1 Like

I have made a Pull Request to the RFC repository: rust-lang/rfcs#3471.

Edit: moved this comment on GitHub

Why would it force the maintainer to do that? "Scheduled" is not "promised". I think it's common enough to schedule, for example, a meeting at work, or a visit with a physician, and then to postpone it.

2 Likes

You don't mention how it would be used by rustc or cargo, beyond printing a string. If it's just a text, then you can already do:

#[deprecated(note = "this unit struct will be removed in version 0.3.0")]
4 Likes

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.