Rust Internals
internals.rust-lang.org.web.brid.gy
Rust Internals
@internals.rust-lang.org.web.brid.gy
Discussions around the design and implementation of The Rust Programming Language

[bridged from https://internals.rust-lang.org/ on the web: https://fed.brid.gy/web/internals.rust-lang.org ]
A plan to move most of `std::io` to `alloc`
Thanks everyone for your replies! bjorn3: > Default impls are the hardest part of EII to implement. Several targets don't support weak symbols at all or have rather buggy support. This includes the windows-gnu targets where weak symbols are non-functional and windows-msvc where exporting weak symbols from dylibs requires a hacky implementation. I am surprised to hear that. Doesn't `#[global_allocator]` work on these platforms as well? What prevent using the same technique with EII? josh: > I think we could manage to move a fair bit of `io` to `alloc` or even to `core`, by relying on the fact that all of `core`/`alloc`/`std` are one coherence domain. This is interesting to know indeed. While splitting the definition of `io::Error` would be complex, splitting the methods could enable something like "manual EII" (where constructing an `Error` from raw OS error would require providing the necessary functions). scottmcm: > I also wonder how much most of the code involved in these _traits_ really care what the error types are? For example, could we have a `GenericRead` with an associated `Error` type, so that std's `Read` is just `trait Read : super GenericRead<Error = io::Error> {}`? I don't think so. Adding a supertrait to `Read` would be a breaking change, and blanket impls from `GenericRead<io::Error>` to `Read` would be complex with the exiting `impl<R> Read for &mut R`. I think that it would require something like specialization lattice? Also code using these traits often rely on the fact that they can create a custom `io::Error`. Just in `std`, you have instances of this in `Read::read_exact`, `Read::read_to_string`, `Write::write_all`, plus OOM handling in `Read::read_to_end` (and probably more). And it would be nice to keep having these specific ones constants. Additionally, I fear that creating new traits would split the ecosystem. Overall, I think that doing the work to migrate the existing traits is easier and better than working around it. kornel: > The `std::error::Error` got split to be possible to expose it in `core`. Maybe `io::Read`/`io::Write` could get a similar treatment? Note that this is the `impl` block of the **type** `dyn std::error::Error` that is split, not the definition of the **trait**. That is why my proposal is only to migrate to `alloc` for now.
internals.rust-lang.org
November 28, 2025 at 10:22 AM
The memory order of the `store` to `weak` in `is_unique` is over-strict #149376
I expand the code of using `is_unique` and `downgrade` as something like the following, which models that there are three `Arc` instances. use std::sync::atomic::{AtomicUsize, Ordering}; use std::thread; fn main() { let strong = AtomicUsize::new(3); let weak = AtomicUsize::new(1); thread::scope(|s| { s.spawn(|| { // t1 if weak .compare_exchange(1, usize::MAX, Ordering::Acquire, Ordering::Relaxed) .is_ok() { let unique = strong.load(Ordering::Acquire) == 1; // #0 weak.store(1, Ordering::Relaxed); // #1 assert!(unique == false); } }); s.spawn(|| { // t2 let mut val = weak.load(Ordering::Relaxed); loop { if val == usize::MAX { val = weak.load(Ordering::Relaxed); continue; } if weak .compare_exchange(val, val + 1, Ordering::Acquire, Ordering::Relaxed) .is_ok() { break; } } strong.fetch_sub(1, Ordering::Release); // #2 }); s.spawn(|| { // t3 let mut val = weak.load(Ordering::Relaxed); loop { if val == usize::MAX { val = weak.load(Ordering::Relaxed); continue; } if weak .compare_exchange(val, val + 1, Ordering::Acquire, Ordering::Relaxed) .is_ok() { break; } } strong.fetch_sub(1, Ordering::Release); // #3 }); }); } Merely change `weak.store(1, Ordering::Release);`(in the original implementation) to `weak.store(1, Ordering::Relaxed);` Assuming the `CAS` operation in `t1` succeeds, that means the loops in `t2` and `t3` cannot exit except that one thereof reads `#1` and the other reads the value written by the RMW operation that reads `#1`. The question is whether `#0` can read `1` and the assertion fails. Assume that `#0` reads `1`, because the initial value of `strong` is `3`, this implies that `#2` and `#3` both happen before `#0`, which in turn implies that the load part of `weak` in `t2` and `t3` both happen before `#1`. According to [intro.races] p13 > If a value computation A of an atomic object M happens before an operation B that modifies M, then A takes its value from a side effect X on M, where X precedes B in the modification order of M. `#1` is not visible to the load part of `weak` in `t2` and `t3`, so the loop in `t2` and `t3` cannot exit; this means, `#2` and `#3` cannot be reached by the corresponding control flows in their respective threads. This contradicts the assumption that `#0` reads `1` because `#2` and `#3` won't be executed in the lifetime of the program; otherwise, it would violate [intro.races] p10 > The value of an atomic object M, as determined by evaluation B, is the value stored by some unspecified side effect A that modifies M, where B does not happen before A. More generally speaking, any thread claiming that its RMW operation on `strong` would be read by `#0` is impossible, because its `CAS` operation on `weak` does not expect `MAX`, and `#1` is not visible to the load part, so its loop cannot exit. And no other RMW operation on `weak` that increases count can precede it; otherwise, the modification order of `weak` is invalid 1 < MAX < weak_store_1 < RMW_weak_a < ... < RMW_weak_current < ... < weak_store_1 So, `#0` cannot read `1` and `2`; however, it can only read the initial value `3`. Is my analysis right? If the target is, as described in the comment of the source code, to check whether the thread holds the unique strong, I think the memory order used for the case is over-strict, and the memory order of the store to `weak` that writes `1` can at least be `relaxed`, IIUC.
internals.rust-lang.org
November 28, 2025 at 11:24 AM
Complex Numbers (Pre-ACP)
MLIR does
internals.rust-lang.org
November 27, 2025 at 1:48 PM
Re-opening deprecating Option::unwrap and Result::unwrap
ryanavella: > I would like to use `.unwrap()` as a terser `unreachable!()`, but I work with developers whose usage of it suggests they think it is more like `panic!()`. You could argue this was one domino in the Cloudflare chain-of-dominos: a team environment where each developer had different ideas of what `unwrap` means. I feel exactly this way, there's many meanings for `.unwrap()` and each person has their own favorite meaning. Here is another data point: the lang team feels like `.unwrap()` exists largely for quick and dirty code, that is, code that disregards failure modes to simplify development. Which I think differs from the point of view that `.unwrap()` is a sugar for `unreachable!()`, and thus has a place even in code that thoroughly checks and recovers from errors: it's to be used when there is absolutely no way an error could happen, but the type checker isn't smart enough to figure it out. tczajka: > Plenty of functions will panic when their preconditions are not satisfied. It's part of the regular function contract that a function will panic when you give it invalid inputs. So I don't think adding `_or_panic` or `_or_unreachable` here helps. I think many APIs panic because unwrapping is too verbose. I mean, panicking because the input is invalid due to programming error is ok and expected. But if the input comes from the user, it's probably better to not panic but return `Result`, because I want to display this error to the user and ask him to try again. (or else, to prevent a panic you need to validate an input before passing it to the function that will validate it _again_ - a situation that generally is solved by the "parse, don't validate" pattern) But APIs generally don't know where their inputs come from. so it's somewhat common for a Rust API to not decide what's more appropriate, returning `Result`/`Option` or panicking, and thus provide both variants. Which would be probably redundant - if you want to panic, why not call the non-panicking variant and unwrap it? Except that the verbosity of sprinkling `.unwrap()` makes the code harder to read, so people prefer calling a panicking API.
internals.rust-lang.org
November 26, 2025 at 11:27 PM