pre-RFC? documentation markers for backwards compatible changes


#1

I was starting to write a RFC to add some markers for const fn, but then I realized that was only the tip of the iceberg, and that I might as well try to address the problem more generally.

The rust standard library documentation does a great job indicating in what rust version features, types, methods have been added, so that developers can make informed decisions whether they can use APIs based on the baseline rust version they are targetting for compatibility.

However, it stops there. Developers can read when a particular method was added, but not when the particular signature they are looking at was.

For example, the nightly documentation for Vec::new says its signature is:

pub const fn new() -> Vec<T>

meaning one can do:

static mut MYVEC : Vec<u8> = Vec::new();

… but it doesn’t tell that const was added in 1.27.0. So this code is not valid before that version.

But const is not the only change that can happen to APIs: they can be made more generic than they were when they were first introduced, thanks to features like generics or default trait parameters.

I don’t have concrete examples to give, but consider the following:

fn foo(s: &str) { ... }
fn bar<T>(t: Vec<T>) { ... }

The following are backwards compatible changes:

fn foo<T: AsRef<str>>(s: T) {... }
fn bar<T, A: Alloc = Global>(t: Vec<T, A>) { ... }

I’m sure there are already plenty of examples of such changes in the standard library. One that comes to mind is how the PartialEq trait changed from being trait PartialEq to being trait PartialEq<Rhs: ?Sized = Self>, but that was before 1.0.

When I first thinking about this, with only const in mind, I was thinking we could extend the #[stable] attribute to add the information, but considering the latter examples, I’m not sure anymore. Maybe a documentation convention about how to document those changes is what’s needed here. But maybe the since in the #[stable] attribute should be changed whenever an API is modified, too. So that the documentation clearly indicates that the documented API is available in that version, and then the documentation itself can go on to say that simpler versions of the API were available in older versions.


#2

I propose:

#[stable(feature = "rust1", since = "1.0.0", modified = "2018-05-06")]

(or maybe a version number, but that become harder to track…)


#3

@Centril That’d mean that only the one modification (the last?) can be listed.


#4

Yeah; if you wanted more, you could potentially do something like:

#[stable(feature = "rust1", since = "1.0.0",
         modified("2018-05-06",
                  "2020-03-10",
                  ..)
        )]

#5

A date doesn’t seem usable. It would presumably be the date the change occurs, but that would only indicate when the change happened in nightly. Documentation readers would then have to guess what release that corresponds to, so, add 6 weeks and look for the release that happened after that.

The more I think about it, the more I think changing the since to reflect the version in which the current signature, combined with some standardized documentation pattern indicating the history of the API, is better overall. It also doesn’t require any change to rustdoc to make the information appear usefully to readers.


#6

Hmm… Couldn’t rustdoc figure out what version the date corresponds to if it has access to the intervals? That seems easiest for libstd devs and best for users who read docs.


#7

How is that easier for libstd devs? Actually, dates are presumably worse because the day someone does a change is not the day the PR is merged. At least with a version, you have a 6 weeks window that is hard to miss, and the only risk is for things happen close to the switch to beta (but then, since already has the same risk).


#8

Oh right; My bad! I didn’t consider this.


#9

So, I think this idea is interesting, but you all have already gotten into some of the issues here… I’m not entirely sure how to implement this, even if I think the concept is a good one.


#10

I think something like this could work:

#[stable(feature = "rust1", since = "1.0.0", 
    updated(since = "1.1.0", note = "`forget` is now safe"),
    updated(since = "1.44.0", note = "`forget` is now a const fn"),
]
pub const fn forget<T>(t: T) {

Alternatively, since this marker is mainly for documentation, we could continue abuse the #[doc] attribute:

#[stable(feature = "rust1", since = "1.0.0")]
#[doc(updated(since = "1.1.0", note = "`forget` is now safe"))]
#[doc(updated(since = "1.44.0", note = "`forget` is now a const fn"))]
pub const fn forget<T>(t: T) {