[HN Gopher] Changing the Rules of Rust
       ___________________________________________________________________
        
       Changing the Rules of Rust
        
       Author : kevincox
       Score  : 169 points
       Date   : 2023-09-17 13:49 UTC (9 hours ago)
        
 (HTM) web link (without.boats)
 (TXT) w3m dump (without.boats)
        
       | nynx wrote:
       | I'm actually glad the Move and Leak didn't get in. They both have
       | fairly niche usecases but they'd have to be spattered all over
       | basically all rust code.
        
         | jeff-davis wrote:
         | Why do you say they'd have to be splattered over basically all
         | rust code? The post compares them to Send and Sync, and I don't
         | have to use those very frequently (though I don't write a lot
         | of rust code, either).
        
           | nynx wrote:
           | People use Arc and Rc all the time, which would need +?Leak
           | annotations. Things like Option would, I think, also need +
           | ?Move + ?Leak annotations.
        
             | bryanlarsen wrote:
             | If it is an unobtrusive as the existing ?Trait annotations
             | such as ?Sized it's not very much of a burden.
        
             | [deleted]
        
           | mlindner wrote:
           | Because features are always used in the worst (most evil?)
           | way possible by inexperienced programmers. One of the
           | benefits of Rust is that it makes many evil things either
           | impossible or easily detectable.
        
       | hedora wrote:
       | The lack of a Leak-style trait is so painful for me that I'd
       | strongly consider switching to a different language that was
       | basically just rust + Leak. (Adding move and removing pin would
       | be nice to have too.)
       | 
       | The problem with not having Leak is that it forces all futures to
       | be 'static, which effectively disables the borrow checker
       | whenever you cross an async spawn boundary.
       | 
       | Arc is the standard solution, but, for multi-threaded code, it
       | can be expensive (though also more predictable, in fairness) than
       | having a GC.
       | 
       | The only other solution (which I prefer) is to use an unsafe
       | version of spawn that assumes the future it returns will not be
       | leaked.
       | 
       | This certainly breaks the rust promise of supporting safe, zero
       | cost abstractions.
        
         | sabageti wrote:
         | I have seen many comments like this, and I would like to
         | understand better I feel like I'm missing something. In my
         | daily use case(web api) of async rust we use rarely spawn. What
         | are the use case of intensive spawn usage ?
         | 
         | And there is tokio::spawn_local() that doesn't require Send
        
         | scottlamb wrote:
         | > Arc is the standard solution, but, for multi-threaded code,
         | it can be expensive (though also more predictable, in fairness)
         | than having a GC.
         | 
         | Do you have measurements to back that up? I would expect it to
         | be a lot cheaper than GC because GCed language such as
         | Java/Go/etc. GC every heap allocation (and Java in particular
         | doesn't have unboxed objects, so there are a _lot_ of heap
         | allocations!) where Arc <RequestState> would bundle a bunch of
         | things together.
         | 
         | That said, I'm not in love with the Arc solution either. It's
         | not just the performance impact. The programming model of
         | scoped concurrency is just much more pleasant. Giving up that
         | and then also having to deal with putting everything into an
         | Arc, and likely also then wrapping it in a MutexLock, even when
         | it should really be one thread at a time dealing with the value
         | in question, just doesn't do anything good for readability and
         | writability of code...
        
           | charcircuit wrote:
           | >because GCed language such as Java/Go/etc. GC every heap
           | allocation
           | 
           | GC is not run on every allocation.
        
             | o11c wrote:
             | They don't run GC, but they subject the extra pointers to
             | GC, whereas good RC languages generally support values.
             | Doing nothing is usually cheaper than doing something.
             | 
             | This is more a Java problem than a GC problem; C# supports
             | values just fine, and some heroic JVM-targetting languages
             | also manage it.
        
             | scottlamb wrote:
             | Are you interpreting "GC every heap allocation" as "each
             | heap allocation causes an entire cycle of GC"? That's...not
             | what that phrase means.
        
               | charcircuit wrote:
               | Yes I am. Java's GC can stop the world at any safepoint.
               | It isn't limited to when objects are allocated and it
               | doesn't do it every time.
        
       | j16sdiz wrote:
       | rust is _almost_ as complex as C++. Introducing these new
       | "edition" would grow the complexity exponentially.
       | 
       | I hope they would think twice about the effects on new learners
       | before even experimenting
        
         | rapsey wrote:
         | C++ versions are practically a new language. Last two Rust
         | editions required no changes on any of my code. Why are people
         | constantly harping on this obviously untrue idea. New Rust
         | versions and editions make the language _simpler_.
        
           | neandrake wrote:
           | This can be problematic when working on a project where
           | updating the version of rust is difficult, limiting what
           | libraries can be used - or ci/cd where devs may not have
           | direct control of the environment. It's less of a language
           | compatibility issues and more deployment logistics issues.
        
           | kaashif wrote:
           | > C++ versions are practically a new language. Last two Rust
           | editions required no changes on any of my code.
           | 
           | This juxtaposition kind of makes it seem like that new C++
           | versions did require changes to your code, did they?
           | 
           | My understanding is that C++ maintains backwards
           | compatibility.
        
             | rapsey wrote:
             | Rust editions are allowed to break backwards compatibility.
             | If you have 2015 edition code and change your Cargo.toml
             | and set it to 2021 edition, the code may no longer compile.
             | 
             | My code went from 2015 to 2018 and 2021 by simply changing
             | Cargo.toml. No code changes were necessary and it was still
             | completely idiomatic code. Nothing was deprecated or even
             | outdated.
             | 
             | An idiomatic C++11 solution is not likely to be the
             | idiomatic C++17 solution however. Every version of C++
             | makes deep changes to the actual language and adds a ton of
             | features. Whereas Rust versions and editions tend to make
             | the language simpler as they generally remove limitations.
        
               | Conscat wrote:
               | Every release of C++ since 11 has relaxed limitations on
               | `constexpr`, and several relaxed limitations on
               | `template`. Many other features have had various rules
               | relaxed in certain releases, including `for` loops,
               | lambdas, labels, moves, and atomics.
               | 
               | It's true that there are usually new features which
               | arguably make older code no longer idiomatic, but
               | modernizing code is always optional, so what's really the
               | issue there?
        
         | duped wrote:
         | Rust already has editions. There have been three releases since
         | 1.0.
        
         | weinzierl wrote:
         | _" rust is _almost_ as complex as C++."_
         | 
         | I don't agree, but even if it were true, C++ has so much
         | accidental complexity (because of its legacy) that you cannot
         | meaningfully compare it with Rust, which by and large is very
         | well thought out.
         | 
         | In general, if something is complex in Rust that is because
         | it's in the nature of the problem and not because of some
         | quirkiness in the solution.
         | 
         | Borrowing is the prime example. People find it complex, but
         | just because the C++ compiler doesn't check the rules for you,
         | doesn't mean you have follow them in C++ as well (which is
         | tedious and error-prone) or know exactly why you don't have to
         | follow them (which is even harder).
         | 
         | I admit though, that the _Leakpocalypse_ (which the article
         | talks about, even if it doesn 't call it like that) is an area
         | where accidental complexity lurks.
        
           | cwzwarich wrote:
           | One advantage C++ has over Rust is that there are multiple
           | fairly compliant C++ implementations, and the process of
           | making them created some shared understanding of how the
           | language actually works from an implementation perspective.
           | There are several aspects of the current Rust language that
           | were effectively defined by arbitrary decisions made by the
           | implementors, which aren't really spelled out anywhere
           | besides the code.
        
             | tialaramex wrote:
             | Do you have examples?
             | 
             | I think in many ways Rust is doing a _better_ job of tying
             | down the tricky details than C++ has.
             | 
             | There is no effort to explain how pointer provenance works
             | in C++ at all. The state of the art is if you write a
             | provenance bug, someone from a compiler vendor will gesture
             | at DR/260 which says provenance exists but WG14 (so C, not
             | even C++) declines to explain how it works, and that's all
             | you get. In contrast Aria's "Experiment" (https://doc.rust-
             | lang.org/std/ptr/index.html#strict-provenan...) has gone
             | rather well. The vast majority of people who want to do
             | acrobatics with pointers _can_ do what Aria says you have
             | to do to get coherent behaviour, and by following her rules
             | they can make code which works in Rust.
        
               | uecker wrote:
               | WG14 produced a document clarifying provenance in C
               | (which Aria knew): https://open-
               | std.org/JTC1/SC22/WG14/www/docs/n3005.pdf
        
           | vlovich123 wrote:
           | Pin is another one. I still can't wrap my head around it. I'm
           | sure it's simple and the docs are great, but having read them
           | multiple times I'm not really more clear. I probably need to
           | see a bunch of hands on examples. I wish they'd gone the Move
           | route instead of rushing to 1.0 and then retrofitting in Pin.
           | 
           | But I agree, in general things in rust are a lot more thought
           | out and experiments are relegated to unstable which is fine
           | with breaking changes.
           | 
           | I do wish though that they had a better versioning story in
           | the language for making backward incompatible changes more a
           | matter of course. It's great already for "user space" code
           | via editions, but as the author notes making certain kinds of
           | changes brings up risk and uncertainty because there's not a
           | well trodden path to support changing core semantics in the
           | language and having it work gracefully without needing a
           | migration en masse.
        
             | majewsky wrote:
             | > I wish they'd gone the Move route instead of rushing to
             | 1.0 and then retrofitting in Pin.
             | 
             | That is easy to say, and I don't disagree with the
             | sentiment, but there are valid reasons for setting a 1.0
             | release date and cutting corners to get there. If they had
             | not done that, there probably was a real risk of running an
             | infinite loop of "let's wait for Move to be implemented",
             | "great, now let's wait for Leak to be implemented", "great,
             | now let's wait for..." and a 1.0 never gets put out. Rust
             | may not be perfect, but at least it's stable and people are
             | actually using it.
        
             | cmrdporcupine wrote:
             | Pin used to confuse me, but now it's clearer. It's right in
             | the name: Pin this thing to where it is in memory, never
             | try to move it.
             | 
             | Rust can and does move things around as it feels the need.
             | So you need something like Pin if what you've built can't
             | move.
             | 
             | It's confusing if you're coming from a C++ background
             | because in C++ something only "moves" if you explicitly
             | std::move it. In Rust its effectively the reverse. A crude
             | way of thinking about Rust's call pattern is that it's
             | basically the opposite: as if every call was a std::move
             | operation.
             | 
             | So Pin is basically just saying: you can't move this, don't
             | try. The memory can't be moved. And it's _mostly_ used in
             | corner cases where some kind of explicit memory management
             | is involved.
             | 
             | Most Rust _users_ should not have to worry about it. It 's
             | one of those things that becomes more of a concern if
             | you're writing library code for low level datastructures,
             | or interfacing with C/C++ libraries.
        
               | laurencerowe wrote:
               | > Most Rust users should not have to worry about it. It's
               | one of those things that becomes more of a concern if
               | you're writing library code for low level datastructures,
               | or interfacing with C/C++ libraries.
               | 
               | Don't all Rust users writing async code have to worry
               | about Pin?
        
               | cmrdporcupine wrote:
               | I've never had to and I have stuff that uses async all
               | over. The only time I've seriously had to deal with Pin
               | is for some pretty low level stuff: manual memory/heap
               | management for database datastructures (paged btree) and
               | for some work stuff related to working with a C++ library
               | that uses zeromq, etc. The point being that in both cases
               | these were frobby dark corners where the mechanics of the
               | pinning ends up being hidden from the user.
               | 
               | People writing application code in Rust should not
               | encounter it often.
        
               | hedora wrote:
               | You get to avoid pin until you can't, and then it's
               | because of some terribly unintuitive advanced usage
               | corner case.
               | 
               | For instance, the async_stream crate would be much nicer
               | if you didn't need to pun_mut! the stream instances it
               | provides:
               | 
               | https://docs.rs/async-stream/latest/async_stream/
        
               | brigadier132 wrote:
               | Not really, the async libraries hide the implementation
               | details from you. It's an issue for library developers.
        
       | amluto wrote:
       | I wonder if the new-edition approach could be done by
       | interpreting pre-2024 code as if everything had a Leak bound. So
       | instead of a not-Leak type being disallowed when passed to a
       | pre-2024 module, it would not pass type checking because
       | everything in the pre-2024 module required Leak.
        
         | jeff-davis wrote:
         | The article addresses this: "However - and this is the big,
         | enormous caveat of this section - this would rely on the
         | compiler effectively implementing some kind of unbreakable
         | firewall, to prevent a type that does not implement Leak from
         | ever getting used with pre-2024 code."
        
       | jeff-davis wrote:
       | The Leak trait could be interesting when it comes to C code that
       | calls rust code that calls C code that might longjmp() over the
       | rust code in the middle.
       | 
       | Right now longjmp()ing over rust code is not a great idea for a
       | couple reasons, but because leaks are currently always allowed in
       | rust, it arguably follows the rules at least in some cases (doing
       | so could cause problems, of course).
       | 
       | If the Leak trait were introduced, then skipping destructors
       | would be clearly the wrong thing to do if there are types on the
       | stack that don't implement Leak. That would clearly require some
       | way to either prevent longjmp() from ever jumping over rust code
       | (e.g. always catch it in C code and turn it into a special return
       | code), or find some way to catch the longjmp and turn it into an
       | unwind in rust (e.g. a panic?) and then turn it back into a
       | longjmp to go back into C.
        
         | connicpu wrote:
         | Honestly, longjmp is just extremely problematic for any code
         | that isn't pure C. Even just adding C++ into the mix can easily
         | make longjmp something that can't be used without invoking UB.
         | 
         | "If replacing std::longjmp with throw and setjmp with catch
         | would invoke a non-trivial destructor for any automatic object,
         | the behavior of such std::longjmp is undefined."[1]
         | 
         | [1]: https://en.cppreference.com/w/cpp/utility/program/longjmp
        
         | workingjubilee wrote:
         | You have not described a mechanism or API by which the compiler
         | or programmer could enforce that only Leak types are on the
         | stack and that a longjmp will not traverse `!Leak` types. And
         | the only real way to get this would be to describe an interface
         | that would have to be variadic to allow calling functions in
         | general, as you would have to ensure that all args to a given
         | `impl Fn*` are `T: Leak` for `T0` through `Tn`, and Rust
         | currently lacks variadic generics. At that point, you might as
         | well just do the same for `!Drop`.
        
         | LegionMammal978 wrote:
         | > Right now longjmp()ing over rust code is not a great idea for
         | a couple reasons, but because leaks are currently always
         | allowed in rust, it arguably follows the rules at least in some
         | cases (doing so could cause problems, of course).
         | 
         | In the general case, skipping destructors of objects you don't
         | own (through longjmp(), killing a single thread, etc.) has
         | never been allowed in Rust: at any particular point in the
         | program, you're only allowed to skip destructors of objects
         | that you currently own, or can otherwise gain ownership of.
         | 
         | For instance, the thread::scope() API, after running the
         | spawning thread's closure, uses a destructor* to join all the
         | created threads. However, if a program could longjmp() past
         | that destructor, then the spawning thread could access its
         | variables again while the created threads are still using them,
         | resulting in a data race. (This is the same issue that the
         | original thread::scoped() API had.)                 let mut x =
         | 0;       let mut jmp_buf = JmpBuf::default();       if
         | setjmp(&mut jmp_buf) == 0 {           std::thread::scope(|s| {
         | s.spawn(|| loop { x += 1; });               longjmp(&jmp_buf,
         | 1);           });       } else {           loop {
         | println!("{x}"); } /* the created thread is still changing the
         | value of x! */       }
         | 
         | That is to say, longjmp() has always been a very unsafe
         | operation, which can only be sound if you're in control of
         | every single destructor which it skips. A Leak trait wouldn't
         | change anything in that regard.
         | 
         | * Or rather, a catch_unwind() followed by a resume_unwind(),
         | which is mostly equivalent to a destructor in this context.
        
       | Ygg2 wrote:
       | I think eventually or at some point there might be a need for
       | Rust 2.0 even if it won't be called Rust. Think something like
       | JavaScript and TypeScript. A language that's a super set except
       | the former would be soft deprecated.
       | 
       | I know this is highly unpopular opinion, but I don't consider
       | permanent backwards compatibility a good thing.
        
         | ferfumarma wrote:
         | I agree, and would go even further: that all major versions of
         | languages should be renamed.
         | 
         | Like pearl and parrot, as I understand it.
         | 
         | It completely avoids the chance that "python" will become
         | ambiguous regarding which set of rules it refers to: version 2
         | or version 3.
         | 
         | And the downside is minimal, because anyone can find the most
         | current name online.
        
         | twic wrote:
         | I'm not sure if it is an unpopular opinion; i'm not sure it's
         | an opinion at all, since it seems like a statement of fact - no
         | programming language lives forever, eventually being superseded
         | by newer languages designed with the benefit of lessons learned
         | with the older one.
         | 
         | Except Fortran, Fortran is forever.
        
           | Ygg2 wrote:
           | And Ada, Ada was created perfect.
        
         | Guvante wrote:
         | We still don't have C++ 2.0
         | 
         | And if anybody brings up breaking changes in C++ I will point
         | to how after any of those it sometimes takes almost a decade
         | for everyone to update.
         | 
         | And that was for breaking changes designed to have minimal
         | impact. See how Python 2 is still used in many places 15 years
         | later for what happens if you change how strings work for
         | example.
        
           | tialaramex wrote:
           | WG21 ships a completely new language - very similar to but
           | technically unrelated to the previous one - every three
           | years. C++ 11, C++ 14, C++ 17, C++ 20, and (later this year
           | presumably) C++ 23. It is fascinating that people consider
           | the resulting arbitrary source code breaks to be complete
           | compatibility and yet Rust's choice to offer ongoing
           | compatibility to Rust 2015 edition (Rust 1.0) is somehow not
           | enough...
           | 
           | C++ already changed the definition of std::string, that's why
           | your Linux distro went through painful C++ ABI changes many
           | years ago. They went from one terrible string design to a
           | different terrible string design, ruling out some
           | optimisations GNU can chosen, but mandating optimisations
           | other groups had chosen.
           | 
           | Don't worry though, they didn't standardize an actual string
           | slice type (Rust's &str) until much later, as
           | std::string_view in C++ 17, so most software which didn't
           | actually care about string allocation needed to be thrashed
           | around anyway because there was no vocabulary type for this
           | most obvious thing aside from the hopeless C-style "nul
           | terminated char *".
           | 
           | It's actually hard to over-estimate how terrible they are at
           | this (language standardization). Is it really just that there
           | are too many of them? I find it hard to imagine that just
           | large numbers of people can explain it, plenty of people co-
           | operate to create Rust and the results aren't anywhere close
           | to as awful. Maybe it's the ISO process? I have never
           | participated in ISO standardization of something this
           | complicated, maybe the process is somehow poisonous.
           | 
           | But I think the best explanation is the _culture_. C++ has
           | developed a culture which prizes arcane nonsense.
        
             | kukkamario wrote:
             | Large part of why standard was changed so that GCC's copy-
             | on-write strings became non-standard compliant was because
             | CoW made std::string_view and such extremely hazardous. CoW
             | has its benefits but also huge downsides, and small-string
             | optimisation makes lot more sense with the C++11 move
             | semantics.
        
             | Guvante wrote:
             | C++ isn't a language that anyone uses.
             | 
             | Everyone uses GCC C++ or MSVC C++ or Clang C++.
             | 
             | This includes the committee that makes the standard and
             | requires a working battle tested implementation before
             | inclusion.
             | 
             | So you get a Frankenstein result because that is the input.
             | 
             | And while yes in theory the standards define a kind of C++
             | that works on every compiler but the reality is most
             | compilers don't fully support that for literal years best
             | case.
             | 
             | So everyone uses what is available and writes abstractions
             | to handle the harsh edges if they support multiple
             | compilers.
        
             | Conscat wrote:
             | > ruling out some optimisations GNU can chosen
             | 
             | GNU didn't do any optimizations that are acceptable in a
             | language with move semantics. The new `std::string` is
             | _strictly_ better than the old one. Although there do exist
             | very many superior string containers outside of the
             | standard library. libstdc++ still supports the old ABI, by
             | the way, as `std::__cxx11::string`.
        
         | vimpunk wrote:
         | What parts of Rust would you like changed, though?
         | 
         | I think the current version, with its minor warts, is a very
         | pleasant and consistent experience for the most part -- very
         | much unlike C++. So I'm not sure what I'd change to such a
         | drastic degree that it'd require such breaking changes.
        
           | lawn wrote:
           | async still feels half-baked and not pleasant like the rest
           | of the language.
           | 
           | Now, it may be something that still can be fixed and improved
           | upon, but a Rust 2.0 with nicer async experience would be a
           | big win.
        
           | thayne wrote:
           | Besides the Move and Leak traits mentioned in this article,
           | there are several changes I would make to the standard
           | library if backwards compatibility wasn't a concern,
           | including:
           | 
           | Changing Iterator to make use of Generic Associated Types.
           | 
           | Change std::env::set_var to be unsafe. Because using it in
           | multithreaded program causes undefined behavior for any c
           | code that reads the environment (which includes quite a few
           | libc and posix functions).
           | 
           | Change channels to have a better API that is mpmc and closer
           | to crossbeam.
           | 
           | And probably others I can't think of at the moment.
        
           | Taek wrote:
           | I would definitely want to see async totally overhauled. Also
           | it would be great if major components of the standard library
           | were all version 1.
           | 
           | And if I want to risk going too far, I'd also advocate for
           | refactoring the type system to be slightly less generic in
           | favor of chasing faster compile times.
           | 
           | Also, ideally the lead designer of SafeLang2.0 would be much
           | more heavy on engineering experience than PL theory. Rust
           | made a lot of tradeoffs that were beautiful in PL theory but
           | are annoying to use on a day to day basis.
           | 
           | I honestly think rust is equal parts amazing ideas and
           | mediocre ideas, and I think a slighky less ambitious language
           | would still be almost as safe (world class compared to any
           | other language) but 10x more fun to use.
        
             | gmnash wrote:
             | A complete overhaul of async sounds nice in the abstract,
             | but without exact details it's difficult to see how it
             | would be better than what exists.
             | 
             | Similarly, I'm curious as to what the details of a
             | "slightly less generic" type system entails.
        
         | ysavir wrote:
         | I generally agree with you there. Permanent backwards
         | compatibility has strong early rewards and strong late
         | punishments.
         | 
         | I'd love to see programming languages and frameworks add a
         | "gamma" phase to their initial releases. Alphas can be for the
         | internal development, betas can be for stuff that's being
         | tested for bugs and functionality on a public level, and gamma
         | can be a state of "the code can be used out in production, but
         | the API and functionality may change in the future once we see
         | how the tool is used and can identify and resolve operational
         | bottlenecks". Just a handy state that says "feel free to use
         | this, but keep in mind that the interface will likely change
         | for the better but in a non-backwards compatible way based on
         | your experiences using it".
         | 
         | I'm pretty sick and tired of almost every project needing a
         | version 2 because version 1 was essentially publicly released
         | proof of concept that didn't know better.
        
       | xpe wrote:
       | Since a good number of people will arrive at this thread without
       | a lot of surrounding context, would some of us 'in the know'
       | please share some remarks about:
       | 
       | 1. Who should read this? Who should care? Why?
       | 
       | 2. Help me situate this relative to other Rust commentary, ideas,
       | and recommendations. Where does this sit?
       | 
       | 3. What's been happening in the Rust world that may have
       | motivated _boats_ to write this blog post now?
       | 
       | Context: For many years, I've enjoyed and written a lot of Rust,
       | but I don't necessarily keep up with the 'inner sanctum'
       | conversations.
        
       | kmeisthax wrote:
       | Why would Move/Leak being tied to editions hurt compile times?
       | Presumably old editions would have the required trait bounds
       | added immediately after parsing, and the existing trait machinery
       | would handle that. You'd need extra checks for error messages so
       | you can tell people using !Leak/!Move with old code why they have
       | extra trait bounds, but that's about that.
        
         | Rusky wrote:
         | The idea is that this would be a huge number of extra bounds
         | that need to be processed. This is one of the more
         | computationally expensive parts of the frontend.
        
       | notnullorvoid wrote:
       | Here's an idea, for a kind of soft rollout. Implement a optional
       | feature for 2021-edition crates that allow specifying Leak. The
       | trait has no effect in 2021.
       | 
       | In next-edition:
       | 
       | - 2021 crates that turned Leak feature uses it just as a next-
       | edition crate would. Only things explicitly marked with Leak in
       | the crate can leak.
       | 
       | - 2021 crate that didn't opt-in to Leak feature, automatically
       | marks everything in the crate with Leak.
        
         | kmeisthax wrote:
         | from __future__ import Leak
        
         | bryanlarsen wrote:
         | I think the big caveat mentioned in the editions section
         | (https://without.boats/blog/changing-the-rules-of-
         | rust/#editi...) still applies to your suggestion. The author
         | concludes in that section "I don't know if this is even
         | possible to implement, probably it would have pretty bad effect
         | on compile times at".
         | 
         | Your refinement seems like a nice addition to the author's
         | suggestion, if it is possible and doesn't effect compile times
         | too much. Hopefully somebody smarter than me takes a run at the
         | problem.
        
       | lukebitts wrote:
       | Very interesting read! I really wish we could be unburdened by
       | backwards compatibility so big changes like these are possible,
       | but I realize that would open its own can of worms
        
         | Taek wrote:
         | Honestly backwards compatibility in rust is already half-assed
         | anyway. Trying to use a fixed version of the compiler
         | (important in some contexts such as security software) is a
         | huge pain in the ass.
        
           | mirashii wrote:
           | It's about a 10 line nix file, a 10 line dockerfile, a one
           | line rust-toolchain.toml.
        
           | Ar-Curunir wrote:
           | Backwards incompatibility in the language is different from
           | backwards incompatibility in libraries.
        
           | ekidd wrote:
           | Rust has an excellent story for running _old_ code on _new_
           | compilers.
           | 
           | For example, I've been using Rust in production since just
           | after 1.0, so about 8 years now. I maintain 10-20 libraries
           | and tools, some with hundreds of dependencies. If I update a
           | project with 200 dependencies that hasn't been touched in 3
           | years, that's 3x200 = 600 "dependency-years" worth of old
           | code being run on a new compiler. And usually it either works
           | flawlessly, or it can be fixed in under 5 minutes. Very
           | occasionally I'm forced to update my code to use a new
           | library version that requires some code changes on my end.
           | 
           | I've also maintained long-lived C, C++, Ruby and Python
           | projects. Those projects tend to have _far_ more problems
           | with new compilers or runtimes than Rust, in my experience.
           | Updating a large C++ program to newest version of Visual C++,
           | for example, can be a mess.
           | 
           | However, Rust obviously does not support running _new_ code
           | on _old_ compilers. And because stable Rust is almost always
           | a safe upgrade, many popular libraries don 't worry too much
           | about supporting 3-year-old compilers. (This is super
           | irritating for distros like Debian.) Which if you're working
           | on a regulated embedded system that requires you to use a
           | specific old compiler, well, it's a problem. Realistically,
           | in that case you'll want to vendor and freeze all your
           | dependencies and back-port specific security fixes as needed.
           | If you're not allowed to update your compiler, you probably
           | shouldn't be mass-updating your dependencies, either.
           | 
           | Basically, Rust got so exceptionally good at running old code
           | on new compilers that the library ecosystem developed the
           | habit of dropping support for old compilers _too quickly_ for
           | some downstream LTS maintainers. And as a library maintainer,
           | I 'm absolutely part of the problem--I don't actually care
           | about supporting 5 year old compilers for free. On some
           | level, I probably should, but...
        
             | sitkack wrote:
             | I think the answer is that when you break compatibility for
             | an old compiler, you rev the major version. The folks on
             | the old compiler then get to use the old library forever.
             | Yeah, they can pin (and should) but it makes it a little
             | cleaner.
        
               | kmeisthax wrote:
               | Is it a breaking change to not support an old compiler
               | when the new compiler is the same major as the old one
               | was? Transitive dependency upgrades that bump minor don't
               | trigger major version bumps in downstream users. Why
               | should compilers be different?
        
               | sitkack wrote:
               | You would opt in to the future, not have to pin the past.
               | It would give the ecosystem more stability.
        
               | MereInterest wrote:
               | Except that so few people are using older compilers, that
               | tracking exactly when features were introduced becomes
               | burdensome to the developer. For languages that have a
               | few large releases every decade, such as C++, this is
               | reasonable. As a C++ developer, I can remember that
               | `std::unique_ptr` requires C++11, `std::make_unique`
               | requires C++14, structured bindings require C++17, etc.
               | It's tedious, but doable, and can be validated against a
               | specific compiler in CI.
               | 
               | For languages with more frequent releases, that just
               | isn't feasible. The let-else construct [0] was stabilized
               | in 1.65 (Nov 2022), default values for const generics
               | were stabilized in 1.59 (Feb 2022) [1], and the const
               | evaluation timeout [2] was removed in 1.72 (Aug 2023).
               | These are all reasonable changes, released on a
               | reasonable timescale. However, when writing a `struct
               | Position<const Dim: usize = 3>`, I don't check whether my
               | library already uses language features that post-date
               | 1.59, and I don't consider it a backwards-compatibility
               | breakage to do so.
               | 
               | Incrementing the major version for a change that is
               | source-compatible on most compiler versions (e.g. the
               | first use of let-else in a project) just isn't justified.
               | 
               | [0] https://doc.rust-lang.org/rust-by-
               | example/flow_control/let_e...
               | 
               | [1] https://blog.rust-
               | lang.org/2022/02/24/Rust-1.59.0.html#const...
               | 
               | [2] https://blog.rust-
               | lang.org/2023/08/24/Rust-1.72.0.html#const...
        
             | nwallin wrote:
             | > Updating a large C++ program to newest version of Visual
             | C++, for example, can be a mess.
             | 
             | That's an MSVC problem. MSVC ignored the C++ specification
             | for decades. Now it does follow the spec. So a lot of non-
             | standard code broke.
             | 
             | The transition was pretty ugly, as I'm pretty sure you've
             | figured out. The permissive mode never gave warnings for
             | non-standard code, so you couldn't just fix warnings as
             | they came up. You had to do a fix the world update, which
             | are a dickpain in large codebases. The transition was
             | relatively fast; VS2017 introduced the standard compliant
             | parser, and C++20 _requires_ it.
             | 
             | Honestly MSVC is such a mess. I have a hunch that before
             | this decade is out, Microsoft will just replace it with
             | Clang a la Internet Explorer/Edge/Chromium. That's why the
             | switch to the standard compliant parser was so rushed;
             | they're trying to force everyone to write standard
             | compliant code so that Clang can compile it.
        
           | IshKebab wrote:
           | I've never had any notable issues. I've had far fewer
           | compatibility issues with compiling crates than I have
           | compiling C++ libraries or installing Python packages.
        
         | dijit wrote:
         | Isn't that exactly why Rust has "Editions"?
        
           | Guvante wrote:
           | The post goes into detail with why editions aren't a solid
           | solve.
           | 
           | It does mention that dealing with the pain is the only
           | potential solve.
        
           | Rusky wrote:
           | Editions still have to interoperate with each other, they're
           | not a free license to change anything and everything. In fact
           | the post goes into detail on how they might or might not work
           | for these specific changes!
        
       | minroot wrote:
       | Everytime I see Rust, I feel like terrorism happening in PL
        
       | thayne wrote:
       | > this would rely on the compiler effectively implementing some
       | kind of unbreakable firewall, to prevent a type that does not
       | implement Leak from ever getting used with pre-2024 code
       | 
       | Couldn't this be done just by changing how 2021 code is compiled,
       | such that:
       | 
       | All types impl Leak
       | 
       | All generic type parameters have a Leak constraint added
       | 
       | All trait objects are changed to include "+ Leak"
       | 
       | In other words, treat pre 2024 code as if it was the equivalent
       | code in the presense of the Leak trait, requiring that all types
       | are Leak.
        
       | Klasiaster wrote:
       | I think a "linter" that does formal verification would be a good
       | solution. People use things like clippy already and my hope is
       | that using one of the verification tools such as Kani could
       | become equally widespread for checking common things like absence
       | of panics and leaks.
        
       ___________________________________________________________________
       (page generated 2023-09-17 23:01 UTC)