Pre-RFC: Adjust default object bounds

I would like to propose a (minor) breaking change to the language. This change is a tweak to the object lifetime rules. It has been evaluated against and found to have minor impact. The pre-RFC is described here:

Thoughts? Obviously, given that this is a breaking change, time is of the essence!


I like the motivation for this change, and if this were before 1.0 I would very much be in favor, so for me this is now largely a question of analysis of the impact. Perhaps a version of the compiler could be produced and hosted for others to test private and/or application code as well? For example I’d be curious how much of an impact this would have building Servo, for example, or possibly Cargo as well.

1 Like

Excellent motivation I think, but I would like to register the idea that while crater is very cool, it’s only testing a small subset of Rust code out there. We could spread the idea “Being on is how you vote”, but that could be very complicated to handle public-relations-wise.

I could certainly put up a version of the compiler. I can also test servo myself (I’ll let you test cargo :stuck_out_tongue:). But while obviously a crater run is not able to tell us an exact number of crates that will break, it seems reasonable to assume it is at least somewhat representative. Which means we can expect a small number of crates “in the wild” to break, but not many.

One negative side is that the fix, while straightforward, is not necessarily obvious from the errors that will result. It may be possible to improve the diagnostics here to help pinpoint the cause, though doing that properly is probably a bigger job.

I’ve had code for which I needed to add the 'static bound to a Box<Trait>, but I don’t think I’ve ever written code in which the reverse would’ve been true. I’ve no opinion on the backcompat aspect.

Do types like RefCell<Trait> or Mutex<Trait> have the same issue as Box<Trait>? They’re not really owned instances like Box<Trait> is. I don’t think it’s technically possible to create, e.g., &RefCell<Trait> yet (it wasn’t last time I checked at least), but I presume it’s something that’s desired since the library was changed to accomodate DSTs in these types.

If it doesn’t have the same issue, would it be possible to somehow make an exception for these, or would these also be bound to 'static by default?

I’m fine with the changes myself, for what it’s worth.

This sounds like a good test case for Rust’s procedure on breaking changes. For example, your own RFC PR 1122 (which, of course, is still open), as currently written / as far as I can tell, doesn’t seem to list any categories of “underspecified language semantics” that would include lifetime defaults, so it would demand that such changes wait for 2.0 except in “rare cases” (is this rare?). But if it is to be done anyway, the question comes up of the “opt out” mechanism described therein only in broad strokes.

As an outsider my preferred approach toward compatibility would be relatively hardline. I’ve stated this in a few places so I’ll avoid being long, but something like:

  • Crate metadata gets a Rust version field; default is current rustc version but release-quality crates expected to specify one explicitly
  • Old version gets you the old behavior - forever. If C, C++, and Java can do it…

Even if that doesn’t happen, I think that (hypothetically) introducing this change without any opt-out or auto fix mechanism (for a relatively simple change like this, they’re basically equivalent), even this close after 1.0, would be a bad precedent. 1.0 was supposed to stop the breakage…

I think that beyond crater, known companies which are using Rust in private (Dropbox, etc – MaidSafe/IronWorks and the others may also have some private code, I don’t know) who do not use Crates or Github should be provided with a patched compiler and asked to report any problems.

Pre-1.0 we were mostly an open source community, with crates either being on Github (later Crates) or confined to a local computer.

Now we have growing adoption amongst startups and other commercial entities, which may not always open source their rust code. It’s also likely that some of these codebases will be large and prone to breakage.

I believe that @brson already has plans for handling this, but since we don’t have that automation set up yet, we should probably tread a bit carefully here.


Forgive me for spamming my RFC PR, but the basic idea – defining a per-crate target version and select language semantics based on that – would make it possible to effect such a breaking change without breaking any build.

People could try out the new version just by updating their target and see if it breaks, with the reassurance that they can easily roll back. All without requiring pre-builds for special parties.

I have so far avoided targetting language evolution with the RFC, restricting the topic to API evolution, but it appears the same idea can be useful here, too.

Edit: Re-reading this thread it appears that this is also @comex’ proposal. It appears great minds think alike (or at least like little minds like mine – now say that three times, quickly!).

As the community is still growing, would such a small, although breaking change, be able to be managed. If the number of crates were small this change could accompany several PR’s to the breaking crates. If there was a channel to test and allow the community to upgrade their own crates where possible, then that might work.

Of course this assumes downstream apps and libs are not fixed to use older crate versions of these libs with the newer compiler, which would be a larger break. With this in mind the time limit for such a change is very small IMHO.

The regression test was 24 days ago, that was even before the release of Rust 1.0 :-/

That's right. I initially was including "opt-in" language partially with an eye towards this particular change, but I decided to remove it and instead push for this change as an exception to the usual rules. As I wrote in my comment on that RFC thread:

One interesting question is whether we will accept other sorts of breaking changes that are not strictly tied to soundness, but just cases where we seem to have gotten something slightly wrong. I have exactly one example in mind at the moment (some details of trait object lifetime defaults), but I can easily imagine that this scenario will arise. Probably best is to just address such things on a case-by-case basis: every good rule needs an exception, after all.

Yes, I'll re-run the test, but I don't anticipate the numbers will have changed significantly. I'm in the process of rebasing the branch now.

Those types would behave the same way as Box from the point of view of defaults. I'm not sure why you say they are not "owned instances" -- they don't move into the heap, that's true, but they do own their content (and I think that they can be considered a "box" in the more general sense of the term).

Even though the motivation to fix this is well founded, I also worry about the precedent this sets.

“Rust hit 1.0, but they still land breaking changes.”

“1.0 was supposed to mean an end to breakage.”


I’m sorry, I worded that poorly, I mean you can’t just have a raw RefCell<Trait>, because it is a DST. It will either be a reference to a RefCell<T: Trait> (or a Box<RefCell<Trait>> or similar), which would be then be actual owner, or it will be Boxed itself.

At least that’s how I’m understanding how DST work at the moment.

I actually don’t see a problem with this as long as there is no actual breakage.

However ensuring this is a tricky business. Just because only N projects on crater break, doesn’t mean there aren’t P >> N projects on github, bitbucket, bazaar or on someone’s disks that do.

If we take backwards-compatibility seriously, we have to slow down to give people time to adapt. Perhaps splitting the change into multiple steps (for example with a period where elision for the specific case causes a compiler warning that elision behavior is about to change) will make the upgrade path smoother.

Here are the binary compilers that this was tested with.

They expire after 60 days and the test was reportedly done 24 days ago. For future reference this information is available in the taskcluster task definition as CRATER_RUST_INSTALLER.

We can provide an updated after Niko rebases.

I've just rebased my branch this morning (and it passes make check locally):

I’ll get a crater run of the new branch started.