[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)