Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

We can’t get rid of it because we have a commitment to not breaking users’ code. There will not be a Rust 2.0.

There have been some small soundness holes in the typesystem that we have fixed, resulting in breakage, but since that’s in the language, that’s the only way. A library API is different. There haven’t been many of these though. We did have some point releases which immediately fixed some library errors that were introduced by a release, see 1.15.1. But that was in a new API, and so the chance of breakage was extremely low. This API has been around for years. It is also not used much, given it’s a *NIX specific extension you have to explicitly import.

Note that upon using it, you’ll get a warning, so everyone will at least be notified.



>> We can’t get rid of it because we have a commitment to not breaking users’ code.

Totally the correct way to go about it, IMO. Thus my comment about deprecation.

>> There will not be a Rust 2.0.

>> Note that upon using it, you’ll get a warning, so everyone will at least be notified.

:/ That I've mixed feelings about. Why not? Is it because of the fiascos between Perl 5/6 and Python 2/3? Deprecations and warnings about using deprecated features are indeed the correct thing to do, but IMO, aren't the whole picture. I think to all the times I went to compile something, and got to watch a constant stream of warnings for it (or worse, got hit by bugs I shouldn't have because the programmer took time to finally suppress that annoying warning pointing to the flaw in his code). A (very occasional) major version bump to show "These things we found out were actually wrong and said that you shouldn't do N years ago? We meant that, those are gone now," strikes me as the Right Thing for a language with a major focus on correctness, like Rust.

But, since I'm here armchair quarterbacking here on HN, instead of getting my hands dirty building a major language, I'm willing to concede that my opinion might be different were it informed by experience.


It’s partially that, it’s partially that systems people are very conservative, and it’s partially Rust’s own history before 1.0. People do view major breaking changes very differently these days due to those specific situations, and some people still think Rust changes daily. Systems languages tend to have a stability timeline of “forever.”

Rust does have a strong commitment to safety, but not an absolute commitment to correctness. These things happen very infrequently. Is it really worth taking an extreme action (which a major language version bump is, especially in the systems space) just to turn a few warnings into errors? Currently, we don’t think so. Maybe in 20 years, when (And if! :) ) there is more than one standard library API that suffers this problem, it would be worth it, but at the current time, it just doesn’t seem to make sense.


I personally feel like this falls clearly into the soundness hole catagory and should be allowed to be removed in a breaking way. I understand the desire to avoid breaking changes, but soundness issues like this chip away at what "safety" in Rust really means.

I think I'd expect to see warnings about the existing safe method being deprecated for a while, then it's complete removal at the time of another edition of Rust. Users of older versions of Rust can still use the removed function, but newer editions ban it.

This could be implemented as a reserved STD function list in the compiler if it must, since as you've mentioned we only have one STD lib atm.


Yes, as I said below, this is technically a thing we’re allowed to remove. Maybe someday we will. But we want to be careful.

Editions cannot make this kind of change, as also discussed below.


I think it could be an edition thing though... just say Rust 2019 will not allow `Command::before_exec`. By turning on edition 2019 you've accepted the new brakages that it imposes on you, and this is one of them.

Never mind the fact that the function is still a symbol in the STD library.

I understand if this is too much complier magic, though it seems like the correct idea to me anyway.

Am I missing something?

P.S. symbol version pinning seems like a great idea, I've even dreamt of having exposed syntax for it, e.g. `Foo::<i32>::bar@v1.3.2(arg1, arg2)`.


Please see the discussion below; I already discussed this at length :)


Just so I'm clear, are you saying then that it's not possible or desireble to have Rust 201X code link against Rust 201Y STD? Or would that work?

Sorry if I'm not making my thought very clear.


It is not possible to have the standard library be different for different editions; it must be the same in all of them. There’s no #[cfg(edition=“”)] construct.


Can't there be a # [deprecate(untiledition=2018)] to solve this? The standard library contains all the old stuff but it becomes inaccessible in all editions after 2018.


There can not.


Gotcha, thanks for taking the time to explain.

I hope I didn't come off as being to negative about the change. Warnings are still a pretty good solution here.


It’s all good!


Would it be reasonable to introduce a compiler flag that is enabled by default to mark this category of warnings as errors, or would even that need a major version bump?


It’s not really feasible. Maybe in an extreme case, but this just isn’t one. We have done this for fixing language soundness holes, but it is extremely rare. The spirit of the law matters as much, if not more, than the letter.



It’s a policy question, not a technical question. We can absolutely do it technically.


  We can’t get rid of it because we have a commitment to not breaking users’ code. There will not be
  a Rust 2.0.
This is interesting. The C Standard people have, for example, removed “gets” from the C11 standard. Go, too, has exceptions to its Go 1 Compatibility Promise[1], which include security, unspecified behaviour, and bugs, both in the language and in the standard library. I don't remember for sure, but I think there were things removed from C++ specifications as well; please correct me if I am wrong. Did Rust decided to never break stdlib compatibility because the mechanism of editions was considered from the start, or was this rather ad-hoc?

[1]: https://tip.golang.org/doc/go1compat#expectations


We do reserve the right to make changes for soundness holes. In theory, you could argue that this is a soundness hole, and so we can remove it.

We have to do such things extremely judiciously though. Users can only tolerate so much breakage, even if you say “we did say we reserved the right to do this.” This API is just so rarely used that it was judged not a good time to pull the “technically we are allowed to do this” card.


C, C++, Common Lisp, and others have the benefits of an ANSI standard. Any compiler that says it supports the standard, supports it, regardless of compiler version or breaking changes in future standards. My C89 code will continue to work as long as a compiler supports that standard. Worse comes to worst, I can create my own implementation.

When you don't have a standard, you're left at the mercy of languages promising not to break stuff. If they do anyway, your options aren't as clean as just sticking to the older standard.


C++ has changed some very old or rarely used things, like `auto`. It's very rare though.


> There will not be a Rust 2.0.

Isn’t Rust 2018 effectively Rust 2.0 (since it introduced new keywords and requires changes to old code if a user opts in to Rust 2018)? So the function could be made unsafe in Rust 2021 (or whatever it’s called).


No, because it is opt in. All existing code keeps compiling as-is.

The standard library cannot change with editions for exactly this reason. It’s compiled with a particular edition, like any other crate, and so can’t work differently in different editions.


I still feel that there is more to language compatibility than "Can rustc run it with the right setting". Rust 2018 is a new version, the fact that it's not called 2.0 is just semantics. And I still believe "version interopability" would have been a better term than misusing "backwards compatibility".


All existing code continues to compile as-is. That’s the definition of compatibility.


Can I copy a module from an old project into a new crate and it will always work without adjustments?

What about code examples from StackOverflow, or Github issues? Will these work without adjustments in my local project?

Will a future syntax highlighter always be able to highlight the code in my old Rust articles?

Will future tooling always be able to read my current code?

Can I make editor macros and snippets that will keep working in every version of Rust?

If I can write code in one crate and it will compile, but then write it again in another, newer crate and it doesn't, then it isn't backwards compatible. Plus, as noted above, "code will keep compiling" isn't even a guarantee that the language team gives you.


1. Depends on the new project, of course. Just like any language.

2. Same thing. You can construct an example that breaks any language in existence.

3. Yes.

4. Yes.

5. Yes.

6. You’re using an idiosyncratic definition, so yes, it’s not the same. Backwards compatibility is about the same thing continuing to compile, not about changing things and expecting it to still compile; that’s forwards compatibility.


Once again something is at odds here. You give resounding "Yes" comments, but in the backwards compatibility discussions I had with language team members, those "Yes" comments were actually "not really our problem" answers.

I also don't know how current code running in a future compiler is forward-compatibility. And the Rust guarantee only kind-of holds if you define "code" as "crate". Everything outside of or crossing that boundary is not compatible.

To clarify and bring it to an example: Are you guaranteeing that `(a<b, c>(d))` will always be parsed as a tuple with two comparisons? Because if so I believe you're the only one.


When he says "version" he means "version". Are you taking "version" to mean "edition"? No matter how many version upgrades the rust compiler gets it will still compile 2015 edition (and 2018 edition) code.


An edition is just another word for version. If the language were truly backwards compatible, you wouldn't have to tell tools like `rustc` which version/edition your code is written in.


You’re conflating Cargo, Rust (the language), and std/core libraries, and rustc (the compiler). This is one of the difficult parts of Rust and when people say “there won’t be a Rust 2.0” they don’t clarify which part they’re talking about.

Given the tight relationship of all of these parts (despite each one being capable of being individually versioned), it would seem feasible to add a cfg attribute for the Rust edition, and then make the function unsafe if the user had opted in to Rust 2021 (or whatever).


I am not conflating anything. The compilation model of the language does not permit such a thing, independently of implementations of any of those pieces. This is a crucial aspect of the design of the language and the edition system.

Think of it this way: crate A uses edition 2015. Crate B uses edition 2018. There’s only one copy of the standard library. It can’t be compiled both ways.

If, in theory, we let you have multiple copies of the standard library, maybe that could work, but that’s not possible nor desirable for a host of reasons.


With all due respect, I do think you're conflating the way things are with the way things have to be.

glibc has a similar commitment to never breaking user code, and yet symbols have been removed from glibc! The trick is to use "symbol versioning"; code compiled against old versions of glibc is transparently rewritten to use the old versions of symbols, while code compiled against new versions is rewritten to use the new versions of the symbols. [0][1]

You can imagine something similar for the Rust standard library, whereby both the unsafe and safe versions are provided, and packages are transparently rewritten to use the correct version based on their specified edition. You don't need two copies of the stdlib; just of the symbols that have diverged. In this case, you wouldn't even need two versions of the symbol in the resulting binary, since the addition of `unsafe` doesn't change the codegen of the function, but merely restricts what code is accepted by the compiler.

It's true that this would require some serious shenanigans in the stdlib, but it's a tractable problem. It's also, in my mind, quite acceptable for the standard library to play games like this, since the compiler, the language, and the stdlib are always going to be tightly coupled, and the complexity can be shielded from users.

All that said, it sounds like the list of deprecated stdlib features is nowhere near long enough to justify adding this kind of complexity.

[0]: https://gcc.gnu.org/wiki/SymbolVersioning

[1]: Note that symbol versioning is kind of a quagmire, but that's because it's seriously underdocumented and practically no developers are aware that it exists. The idea itself is sound.


Maybe we’re just speaking at odds here. The current design of the language does not allow this. A different design may make it possible, but that’s a completely different question.

I think what you’re saying is “it is possible to do this with the correct design” and what I’m saying is “that design has not been proposed nor accepted and so the answer today is “that’s not possible”.” Does that sound about right?


Yes, absolutely! (Although note that I'm not the OP.)


Cool. To be clear, I think that it’s maybe not even possible with symbol versioning, because you need more information than that. The compiler has to know “is this function unsafe or not”, for example, not just “does this symbol exist.” I’m not an expert at symbol versioning, so maybe there’s a way to do this I’m not aware of. There’s also other stuff you’d need, too, I believe... but the real point is, you cannot do it today, and that’s really all I’m trying to say. :)


Yep, definitely. In this specific case of a missing unsafe marker you don’t need symbol versioning at all; you need the stdlib to somehow present two signatures for the same function that the compiler can select between according to the Rust version. It’s only for more complicated cases of API breakage (your function took an i32 but you need to extend it to an i64) that symbol versioning becomes necessary.

And great! All I wanted to point out was that, should the need arise, it is technically possible to tie stdlib function signatures to a particular edition.


Currently when you have crate A with a deprecated function there is no warning until crate B tries to actually use that deprecated function, so clearly there is some place that crate B can look at to see whether a particular method is deprecated, there is even place for a note field that will get printed when crate B tries to access it.

Why would it not be possible to have some additional information that says "from rust edition 2021 onwards this is actually obsolete". Whether crate B merely gives a warning on compilation or an error then depends on crate B's edition.

Sure that means you can still use the method if you really want to (by using older editions), and it also means that if some of your dependencies are still on an older edition you can't prevent it from being used by them, but it would be a higher hurdle to accidental usage.


> Why would it not be possible

There may be a way in which it is possible, but it is not currently possible. We’d have to have a different design.


> We can’t get rid of it because we have a commitment to not breaking users’ code.

This is not really true though. The commitment is about publicly visible and crater-testable code. If you have your own code in-house, there isn't much guarantee unless all syntax/code you use is widely used in public as well. A known change in language grammar will be evaluated on its publicly visible impact, not on its general breakiness.

It's really disappointing to keep reading about guaranteed backwards-compatibility and then being told in RFC discussions that those don't exist.


Cf. `try` becoming a keyword, when there are hundreds of calls to a method named `try` in Servo, one of the largest Rust projects in existence.


However, none of that affects a crate until it opts into the new syntax. So everything keeps building just fine.


That’s not true. We have rules. some of those rules allow us to make changes based on that kind of thing, but it’s still done relatively little.


I've had many discussions and much pain about this topic. Can you tell me what I wrote that was specifically wrong?

If backwards compatibility is guaranteed, turbofish cannot be "fixed". I can't find the link currently, but from my reddit post on this discussion the crucial quote from a language team member was:

> Our bar for doing backwards compatibility breaks has never been soundness fixes. We have in the past done changes given future-compatibility warning with lints and then made such changes without an edition.

The turbofish discussion also contains this quote:

> This RFC technically amounts to a backwards incompatible change without using the edition mechanism. However, as the RFC notes, a crater run was made and the syntax was not encountered at all.


Yes, there are some lang team members that want to change this policy, but it is not the current policy. Those changes were for soundness fixes.

You can see how controversial that RFC was, and for that exact reason.


No, that wasn't for soundness fixes. This is from the discussion surrounding syntax changes like turbofish or chained if-let bindings.

Can you point me towards an authoritative post in the turbofish discussion that says that it can't happen because of backwards-compatibility changes? Because before it was locked it seemed that the language team wanted to go ahead.

I've been repeatedly told by language team members that the policy you're promising people doesn't exist. I would really appreciate it if there was further clarification.


Those things didn’t happen yet. I’m talking about the ones that did happen.

My understanding is that, again, some lang team members want a different policy. That doesn’t mean that it’s actually different.


Where do you get that understanding? All the comments and the summaries I've seen by language team members on this give me a different impression. See this comment from Niko for example: https://github.com/rust-lang/rust/issues/53668#issuecomment-...

So, can you point me to a comment that tells me that the policy still holds and is as you are presenting it?


The language evolution RFC.


How do you square your interpretation with the comment I linked to above?




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: