# Libs-API Meeting 2026-01-06
###### tags: `Libs Meetings` `Minutes`
**Meeting Link**: https://meet.jit.si/rust-libs-meeting-crxoz2at8hiccp7b3ixf89qgxfymlbwr
**Attendees**: Amanieu, The 8472, Tomas Sedovic, David, Nia Espera, Josh Triplett, Chris Denton
## Agenda
- Triage
- Anything else?
## Adding PidFD to FreeBSD
The 8472: Does it make sense to add it there? Then we'd have two projects using it
Josh: If it's similar, that sounds good
The 8472: It's different
Josh: Would it turn out the APIs would diverge sufficiently enough that other parts of the ecosystem would end up diverging in the future? But if that's the case, we could make that functionality
The 8472: Right now we have spawning, killing it, waiting. FreeBSD supports that too. Linux adds more stuff that FreeBSD doesn't.
Amanieu: Is there any downside to unconditionally using PidFD internally?
The 8472: IT's not available on older linuxes. And it consumes FD slots. But it should in general be an improvement for the pid recycling issue.
Josh: If the question is about trying to make the functionality portable across FreeBSD and Linux, the answer is yes. It seems likely we'd expose an extension trait under freebsd that exposes the same API as linux
The 8472: In the public API it would be distinct types reachable via different modules so we could split them in the future or use extension traits.
Josh: In the short term I think we want to add std::os::bsd::child_ex::pidfd (?? todo check), later on we could do something like what we did for linux and wasm and have a common denominator.
The 8472: Tokio wants this to be able to do process exit.
Josh: That's reasonable. Having a PR that does this seems like a decent start.
Amanieu: Alternatively, Command API rework.
## Triage
### FCPs
14 rust-lang/rust T-libs-api FCPs
- merge rust.tf/80437 *Tracking Issue for \`box\_into\_inner\`* - (1 checkboxes left)
- merge rust.tf/106418 *Implement \`PartialOrd\` and \`Ord\` for \`Discriminant\`* - (2 checkboxes left)
- merge rust.tf/116258 *Tracking Issue for explicit\-endian String::from\_utf16* - (1 checkboxes left)
- merge rust.tf/139087 *Fallback \`{float}\` to \`f32\` when \`f32: From\<{float}\>\` and add \`impl From\<f16\> for f32\`* - (4 checkboxes left)
- close rust.tf/136638 *warn on empty precision* - (3 checkboxes left)
- merge rust.tf/98407 *Tracking Issue for \`Exclusive\`* - (1 checkboxes left)
- merge rust.tf/140808 *Implement Default for &Option* - (1 checkboxes left)
- merge rust.tf/141994 *add Iterator::contains* - (1 checkboxes left)
- merge rust.tf/76314 *Tracking Issue for atomic\_from\_mut* - (1 checkboxes left)
- merge rust.tf/125687 *Tracking Issue for \`new\_range\_api\` (part of RFC 3550)* - (3 checkboxes left)
- merge rust.tf/149482 *thread::scope: document how join interacts with TLS destructors* - (3 checkboxes left)
- merge rust.tf/122034 *Tracking Issue for raw\-pointer\-to\-reference conversion methods* - (3 checkboxes left)
- merge rust.tf/99301 *Tracking Issue for \`error\_generic\_member\_access\`* - (2 checkboxes left)
- merge rust.tf/150300 *Constify \`fmt::from\_fn\`* - (3 checkboxes left)
[jackh726 (1)](https://rfcbot.rs/fcp/jackh726), [BurntSushi (7)](https://rfcbot.rs/fcp/BurntSushi), [the8472 (7)](https://rfcbot.rs/fcp/the8472), [Amanieu (5)](https://rfcbot.rs/fcp/Amanieu), [scottmcm (1)](https://rfcbot.rs/fcp/scottmcm), [dtolnay (1)](https://rfcbot.rs/fcp/dtolnay), [nikomatsakis (2)](https://rfcbot.rs/fcp/nikomatsakis), [joshtriplett (5)](https://rfcbot.rs/fcp/joshtriplett)
### (nominated) rust.tf/98407 *Tracking Issue for \`Exclusive\`*
Josh: I ran into a usecase for this in practice. In addition to the Bevy usecase, this is also used in the web ecosystem. Hyper and things built on top of it required Sync when trying to create an HTTP body from something asynchoronous. Using the Sync wrapper mechanism (same logic as `Exclusive`) Hyper was able to drop the `Sync` bound.
Josh: That's further evidence this is desirable. And that we want to stabilize it soon. Two questions: naming and we should eeither provide the `AsMut` trait or the `get_ref` function. But we shouldn't be in an inconsistent state where we use a trait for une and the inherent method for the other.
Josh: We currently provide the `AsRef` trait and not `AsMut`
Amanieu: Okay, the `AsRef` trait is missing from the tracking issue.
Josh: There's a reasonable argument for having the inherent methods. But there are arguments for doing only traits or doing both everywhere.
Amanieu: I'm against having the trait ones altogether. But I'm against the existence of those traits too.
The 8472: Didn't you change your stance on that recently? Stuff taking `AsRef<Path>`?
Amanieu: sure but the point of passing Exclusive by value?
The 8472: It's usually receivers that take `AsRef<T>` which may be different wrappers around `T`. So it's not necessarily Exclusive.
Amanieu: Could we try to form a consensus for only providing the inherent methods?
Josh: We talked about the idea that once we could evolve things more safely, we'd want to traitify more things from stdlib. Is that still roughly the pla n we had consensus on?
David: depends on what evolutionary safety. One problem is adding a new trait vs. just the user writing `as_ref` that would be resolved by ??. That's why I prefer the specifically named function.
Josh: The premise is "if the input type is not what you're expecting it to be, the output might also not be what you expect"?
David: That's part of it. There's also being more readable without having to rely on IDE. And autocomplete impact, too.
Josh: For certain kinds of things, I'd broadly agree with that. But `Exclusive<T>` is similar to `Option<T>` wheer there's no more specific name that you could add. If you're talking about `Option<T>` then the name `as_ref` giving you `Option<&T>` and `Exclusive<T>` is a similar kind of thing -- there's no more specific name you could give to the method.
Amanieu: One concern to `AsRef` definition: it's very brittle for type inference. We've had issues with type inference due to overdependence of AsRef etc.
Amanieu: We have `AsRef` for `String`, `&str`, `Vec` etc. In this specific case, how will `EXclusive<T>` be used in practice. You pass Exclusive and at some point you'll get it back. At no point will the `AsRef` be involved here. You're passing it exclusively and just get it back.
The 8472: The `AsRef<T>` might come up in other APIs that don't expct Sync. They just expect a generic type that `AsRef`s to `T`. And those could take `AsRef` as a type parameter.
Amanieu: Sure but then just call `get_ref`.
The 8472: But we already have the `AsRef` trait and it's used in places.
Josh: I don't think it's likely that `Exclusive<T>` will be used in cases where it'll be completely generic over `AsRef` or something. But any method that has a specified signature and naming makes sense to be a trait.
Josh: Is there any reason to call these `get_mut` vs. `as_mut`? We should use consistent naming with other container types we already have.
Amanieu: For container types, `get` and `get_mut` is the consistent patter
Josh: I just looked, but we do have a lot of `as_mut` as well.
Amanieu: LazyLock has `get` and `get_mut`
Josh: `Option`, `Pin`, `NonNull`, `Result` all have `as_mut`
Amanieu: I guess `as_mut` is more accurate?
The 8472: `Option::as_mut` converts to another `Option`. Do we have `as_mut`s that get reference to the internal type?
Josh: `NonNull::as_mut`
Amanieu: That's kind of a Deref-ish `as_mut`. It seems all the inherent `as_mut` methods transforms the value somewhat
The 8472: That's an argument for adding a trait impl
Josh: `NonNull::as_mut` matches the signature of the types and so it's obviously unsafe
Amanieu: We also have `LazyCell`, `OnceCell`, `Cell`
Josh: `get_mut` on `LazyCell` and `OnceCell` erturn an `Option`. `get_mut` on `Cell` is effectively `as_mut`.
Amanieu: `Writer` returns the underlying writer
Josh: Those are essentially `as_mut`
Amanieu: Arc erturns an `Option`
Josh: I think the strict majority of `get_mut` return some wrapping over the underlying type
Amanieu: just like the inherent (not trait-based) `as_mut` methods
The 8472: For `BufWriter` you want to reference for it rather than the inner type. To prevent the user calling the wrong thing.
David: Those we typically call `by_ref`.
Amanieu: I see a pattern: `as_mut` returns itself, the wrapper doesn't contain much else other than the inner value. But in almost all cases of `get_mut` the type contains something else other than the inner writer.
David: I see this similar to the distinction between `Borrow` and `AsRef`.
Josh: So `as_mut` is: I'm getting the thing that's conceptually the whole type. It may be wrapped in an Option because it has to be. Or it's a borrowed version of the thing. But it's the whole type, there's nothing else you're not looking at. By that argument, it depends on whether `Exclusive` is more `Cell` or now.
Josh: Amanieu, you said `RefCell` and `Cell` contain more than just the type and you argued `get_mut` is the correct name for those.
Amanieu: Could we call this `ExclusiveCell`
Josh: There is no inner mutability
The 8472: SyncCell was proposed?
Josh: We rejected it because it doesn't have inner mutability too.
Josh: I'd argue this is not logically a `Cell`. It's not changing anything about the underlying type in any way which suggests this is something that the `as_mut` naming implies.
Amanieu: I withdraw my arguments, just use the trait. It should be called `AsMut` and `AsRef`.
Josh: Any objection asking for that rename?
David: `as_mut` and `as_ref` without the trait is my preferred option but I'm fine with either.
Amanieu: Every other `as_ref` that doesn't use the trait does it because it needs to change the signature. `Exclusive` doesn't.
Josh: The argument for the inherent function is if we had wanted to explicitly refer to the inner type.
The 8472: That would only make sense if we wanted to have `Deref` on this.
Josh: We talked about having `DerefMut` but ended up on not wanting that even if it were possible. And it's not currently possible.
Josh: The other question: it seems we want to have `AsRef` if you are actually `Sync`. The Bevy folks would want that mechanism. BurntSushi had the concern that the name `Exclusive` wasn't a good fit if we allow that. Can anyone think of a better name that doesn't argue it's exclusive even if it is Sync?
Amanieu: `ExclusiveSync`? You're always going to use this type if you're messing around `Sync` trait bounds. It would help to the word "sync" to indicated that.
Josh: Having the word "sync" in there amkes sense. The name would imply: "this is Sync because I have an exclusive reference to it". When you have an exclusive reference to something, you're allowed to get an unexclusive reference to it.
Josh: +1 for `ExclusiveSync`, it's better than any of the other proposals I've seen. Like `SyncLock` or `CompileTimeLock` etc.
Amanieu: Those don't make sense. I'd go for `ExclusiveSync`.
The 8472: It's already under the `sync` module, does that change anything?
Amanieu: No, most people don't read the module.
The 8472: But we typically don't repeate the name of the module in the type
Josh: I think they mean different things: the "sync" module contains synchronization primitives, "Sync" in the name is about the `Sync` trait.
Amanieu: Consensus?
Josh: We have consensus in this room, but the original concern was from BurntSushi who isn't here. We should ping him to see if this name resolves his concern.
David: I'm not sold on `ExclusiveSync`. Both Mutex and ExclusiveSync turn something into a Sync. Could we name this something more like `MakeSync`.
Josh: I saw at least one person describe it as essentially a "compile-time mutex".
David: But it also doesn't make it exclusive.
Josh: By that argument it's not so much exclusive sync but more sync via exclusive. `MakeSync` is not terrible. If you `MakeSync` something that's already `Sync` it doesn't do anythnig.
Amanieu: This prevents shared access if the type is not sync.
Josh: True but that's hard to encapsulate that in a name. You also want to say that it isn't preventing anything if the underlying type is Sync.
Josh: I'm getting more sympathetic to BurntSushi's suggestion that it's not requiring the underlying tyep te be exclusive.
David: What about `SyncView` -- it's a view of a type. You get to view the underlying type as Sync.
Josh: I think that's the best thing we've heard so far.
Amanieu: Let's write these up in the thread and keep moving.
### (nominated) rust.tf/99301 *Tracking Issue for \`error\_generic\_member\_access\`*
Amanieu: It's still under discussion. It went into FCP, some people raised that it's a terrible API. Which it kind of is?
Josh: Are we talking about it having to go pick through things one-by-one or because it's overcomplicated for what it does?
Amanieu: It's not about the complexity. The contract is rather weak. You can put a string in there and it doesn't actually need anything. Someone raised the possibility that if we just want backtrace, we could add it to core by using externally implementable items?
The 8472: Jane replied that she has more usecases than just backtrace. E.g. `Providable` type that marks types that provide metadata for errors.
Josh: We could provide that trace for `Backtrace` and `ExitCode` in the standard library. That would prevent people from proposing adding a `u32` or a raw pointer.
Amanieu: I'd like to have both provide-by-value and provide-by-ref.
Josh: I think we can resolve that particular concern as there are more usecases than just backtrace. I would support the "providable" usecase. That's a useful trait to have to make it obvious something is a contcrete type not a generic.
The 8472: Since this is specific to Error API it could be something like `ErrorMetadata`.
Josh: We could make it specific there. Though the term metadata is overloaded (e.g. metadata of a wider pointer). E.g. `ErrorProvidable` / `TypeMappable` might work.
Amanieu: I'll resolve the concern and write up the comment.
### (nominated) rust.tf/117729 *Tracking Issue for \`debug\_closure\_helpers\`*
FCP finished. Should be merged?
Amanieu: There's been code size concerns.
Josh: Presumably it'd just be code size concerns if you called it, right?
The 8472: IT's supposed to be used in debug impl. If someone uses a third party code that provides the debug ipml could blow up your code. The library author might not care but the user might consider it too much.
Josh: I'm reading the code size footguns issue: https://github.com/rust-lang/rust/issues/149745
> every use of field_with with a distinct closure monomorphizes all of field_with's code, adding a hundred lines of LLVM IR per call site.
Josh: If we felt like this was the type of thing we wanted to sacrifice performance in favor of code size: write this using polymorphization. `field_with` could be a one-line wrapper that turns it into a `dyn` and have a function that uses the `dyn`.
The 8472: If you're using it for logging, you wouldn't want the speed hit there.
Josh: I agree, just saying this is the standard trade-off.
The 8472: If you write this by hand without the helpers, would it still duplicate the IR?
Amanieu: There are ways of doing this with a trampoline wrapper. `filed_with` is a big function so monomorphizing that could be quite expensive.
Josh: We could say "that's a lot of code to monomorphize" and ask folks to figure out if that could be made more lighter-weight e.g. by using type erasure, reducing the size of `field_with` etc.
Amanieu: Sure, I wan write that up. Consensus: Fix this and as soon as this is fixed we can proceed with stabilization. If there's an issue, we'll review again.
### (nominated) rust.tf/127534 *feat(core): impl Step for NonZero\<u\*\>*
FCP finished. Should be merged?
Amanieu: We approved the ACP 1.5 years ago. There seems to be some concern around ExactSizeIterator
Josh: I see there's a commit in the PR touching ExactSize Iterator. What's the catual concern?
Amanieu: https://github.com/rust-lang/rust/pull/127534#discussion_r1976841532
> do we want these ExactSizeIterator impls to be dependent on pointer size?
Amanieu: This is wrong on 16 platforms I think? We implement ExactSizeIterator for `Range` of `u32`.
Josh: You're right, that would be broken on 16-bit platform. Arguably, we should provide all of these and make them conditional.
Amanieu: We don't do it for `RangeInclusive` and `RangeIter` so this seems like something we've done for backwards compatibility.
Josh: It seems so.
Amanieu: We don't want to repeat the same mistake.
Josh: I don't think it's a mistake. It seems to be used less for perfect correctness and more for best effort. It seems less likely that people would get compiler errors on 32-bit platforms on missing 16-bit. More they'd lose on some optimizations.
Josh: I'm trying to figure out which types they're providing this for.
Amanieu: I think this implementation is fine.
Josh: As far as I can tell, this conditionalizes it on the target pointer width which is fine.
Josh: I'm trying to figure out which cases are providing `ExactSizeIterator`
Amanieu: The tricky one is inclusive range on `u16` which implements `ExactSizeIterator` on 16-bit platforms incorrectly.
Josh: You're right. By the same argument we can't provide ExactSizeIterator for u32 on 32-bit platform because of the full range of u32. But can for `NonZero` because the biggest possible range for `NonZero` fits the size.
Amanieu: Which is why this PR as written is fine. It's not providing any ExactSizeIterators above 16 bits.
Josh: I think we should fix that, but we should have that discussion later alongside the other "do we want to support 16-bit platforms discussions."
### (nominated) rust.tf/149537 *f\*::min/max do not behave as documented for signaling NaN on aarch64, or with optimizations*
Amanieu: I previously suggested making a breaking change here: be more liberal with our NaN handling so on x86 we could use the the `min`/`max` instructions.
Amanieu: There were some discussions on Zulip since. People didn't like the idea. I was thinking of doing that as an edition change instead.
Josh: So, change min/max over edition to do the reasonably efficient behavior. And translate existing calls to min/max to minimum_num/maximum_num which have the slow IEEE behavior?
Amanieu: Yes.
Josh: And the edition migration will only translate calls on floats?
Amanieu: (yes)
Josh: Yes, so that sounds perfectly reasonable to me.
Amanieu: So the next step is to write up an FCP. That's on my list, I'll unnominate this.
### (nominated) rust.tf/149978 `P-lang-drag-1` *deprecate \`Eq::assert\_receiver\_is\_total\_eq\` and emit FCW on manual impls*
Amanieu: It was never intended for this to be stable. It was doc-hidden and people wrote code using this. I don't even know what it does.
Josh: This is the first I've ever heard of this. [...] Why do we even have this?
Amanieu: It's been in since 1.0
Josh: Was this back when our type system couldn't just query this? So we had to check it in runtime?
The 8472: I think it's a compile-time assert
Amanieu: This was discussed in libs meeting in 2017 saying we won't remove stable methods. They're not stable, they're doc-hidden.
Josh: Strictly speaking they are API. We should certainly deprecate them in some fashion.
Amanieu: You need a compile-time assert that every field implements `Eq`. You can't write it in bounds by hand.
Josh: Why couldn't the `derive` just emit a bound?
Amanieu: The bounds are on the fields. It static asserts that each field implements `Eq`.
Josh: You can name the type of the field.
Amanieu: That would require perfect Derive.
Josh: You're right Perfect Derive is where you do it for the types of the field. That explains it a bit.
Amanieu: It doesn't explain why the method is on the trait itself? It could just be a free function. It only asserts that the receiver is Eq. It says "the current deriving infrastructure says that doing it with a method is impossible". That seems very suspicious.
Josh: Yes. I wonder if the current Derive infrastructure doesn't have a way to access `$crate`?
Josh: Anyway, deprecating this and having FCW seems sensible. Hopefully that lets us rip it out in the future.
Amanieu: That's what it expands to:
```rust
#[automatically_derived]
#[allow(unused_qualifications)]
impl ::core::cmp::Eq for Test {
#[inline]
#[doc(hidden)]
fn assert_receiver_is_total_eq(&self) -> () {
// This block contains checks for each field of the struct
// It creates a temporary variable that requires the Eq bound
{
let _: ::core::cmp::AssertParamIsEq<&'b str>;
}
// If 'Test' had more fields, there would be more such lines
}
}
```
Amanieu: Because the `Eq` trait has no methods, you need a body to place the assertions into. And that's what this method is.
Josh: I see.
(the room groans)
Amanieu: Can you emit an anonymous `const` with name `_`?
Josh: Maybe. Or, could you write this using a `where` clause?
Amanieu: Perfect Derive. The types in the bounds need to be public and the types may be private.
Amanieu: Can `derive` can expand to an anonymous constant?
Josh: It can, but you need to eb inside a scope that has the type you're deriving from. Because what if the type of the field is generic or associated type. You have to be able to name the type.
Amanieu: It would have to be a generic anonymous const.
Josh: You also need it to be bound by the same thing that Eq is bound by.
Amanieu: That's not difficult.
Josh: Yes. But we don't have generic const.
Amanieu: We could replace this with a free function.
The 8472: Yes. Replace it with a separate item.
Amanieu: It would be exactly this but a separate function. Just remove the `impl ...` section.
Josh: That should work.
Amanieu: I don't know about hygiene. But it should work?
Josh: You could define a const_block that on the right-hand-side defines a function that's generic.
Amanieu: We have good reasons to make this FCW?
Josh: Yes. I'd propose Libs-api is in favour of FCW and eventually removing it.
Amanieu: OMG https://github.com/Shresht7/codecrafters-redis-rust/blob/31f0ec453c504b4ab053a7b1c3ff548ff36a9db5/src/parser/resp/types.rs#L255
The 8472: Someone commented that this pops up with autocomplete that fills in the methods.
Amanieu: I'm happy to move this to FCW. It has default implementation, you don't implement this manually. If you do, your code is almost certainly wrong. Do we FCP this?
Josh: I think we should do a Libs API on this.
Josh: Lang FCPs FCWs in general so we should do that too. We should just do a joint FCP.
Amanieu: Alright.
### (nominated) rust.tf/rfc3892 *Complex numbers*
Josh: We'd discussed the idea having complex numbers as a vocabulary type in the standard library. With a minimal implementation for basic operator types (this need to exist because of orphan rule) and leave the rest to the ecosystem.
Josh: We had a concern at a time that the RFC was adding a few more things that weren't needed, like polar coordinate handling and similar. We gave the feedback asking for it to be removed.
Josh: Looking at the RFC, they marked some of the things resolved and not did them. But for most of the time they did do what we asked. We just need to make sure it's fully ready.
The 8472: This will also promise FFI compatibility, right?
Josh: Yes.
Amanieu: What needs to be done?
Josh: Move a few things from current state to Future Work section and do an FCP merge. I'd propose FCP merge and concern to move polar and similar to Future Work.
The 8472: I think we should wait with the FPC so people don't have to review the RFC mulitple times.
Amanieu: My concern is this API shouldn't go far enough and it should expose everything num-complex was.
Josh: I'm in favor of adding all that, but I wanted to be incremental. And we can add the other bits as soon as we can. But I'd love to do those incrementally.
Amanieu: Sure, let's add those as unstable for now.
Josh: Yes, exactly.
Josh: I just made the change to the RFC by making the suggestion and applying it. I believe it's in the desired state now.
### (waiting on team) rust.tf/146099 *Stabilize \`debug\_closure\_helpers\`*
Amanieu: It's been discussed, not waiting on team anymore.
Josh: Yes, we asked for it to be optimized.
### (waiting on team) rust.tf/147400 *TryFrom\<integer\> for bool*
FCP finished. Should be merged?
Amanieu: We approved this.
Josh: We reviewed this and said this is the obvious desired behavior and many people wanted it.
The 8472: The main issue was commandline shell, but we basically ignored that. People may have different expectation there.
Josh: Sure.
### (waiting on team) rust.tf/147781 *Tracking Issue for clamp\_min and clamp\_max*
Josh: Someone set a PR implementing `clamp_to`
Amanieu: I'll remove `waiting-on-libs-api`
Josh: Do we think this is ready to stabilize once the change has been made?
Amanieu: I'll have a second look at it.
### (waiting on team) rust.tf/148024 *Handle OOM when writing to \`Vec\`*
Amanieu: I thought we decided not to do that in the past?
Josh: I think when this says writing it literally means the `Writer` trait
Amanieu: Realistically, this is never going to happen in practice.
The 8472: This could happen in e.g. a JS runtime and someone tries to allocate an enormous actor and they'll run out of memory.
Amanieu: Yes but you can run out of memory in many ways.
The 8472: There's a difference between a huge allocation and a lot of small ones.
Josh: This API is taking an advantage of an existing error return. On the other hand, we're in the middle of moving write to vectors to a trait that will never error. So you don't even have to check.
The 8472: Isn't that just for string formatting?/
Josh: Also for Vec. And String is a Vec too.
Amanieu: We have the capability of using `format!` to write to `Vec<u8>`.
The 8472: But that's formatting. This is about writing bytes directly.
Amanieu: `write_fmt` is not listed here but it's a default implementation that calls `write_all`.
Josh: Which means if `write_all` fails, `write_fmt` will fail.
Amanieu: But with the new trait it would skip over all this and it would use the Write API.
Josh: Originally, we'd implement `WriteFmt` to implement `Error<!>`
The 8472: For `append` and `extend` etc. we'd have `try_extend` etc. But this one is something we can make fallible without changing the API
The 8472: I link prior art here: https://github.com/rust-lang/rust/pull/148024#issuecomment-3599282938
Amanieu: Those are all `read_to_end` and `read_to_string` where the size comes from outside of the program. There you could run out of memory because maybe it's a virtual file with a massive size. But for `write` I don't think that applies. Because all the data is already in memory. You're at most doubling the memory usage.
The 8472: Yes, but people do frequently run out of memory on 32 bit systems.
Josh: And on 64-bit system depending on how the memory is managed. But sure, on 32-bit you might run out of address space.
Josh: Strictly speaking this change is correct. But in almost every other API we've set the API that vectors and strings are infallable and doing this change will likely break poeople
Amanieu: It won't break people. But it's kind of like like the `close` thing: the ecosystem would gravitate to "use `write` because that lets you handle errors".
The 8472: I don't think so, this would let people do the fallible reserve and then do the infallible write.
Josh: The concern I had was: someoen was writing e.g. a runtime for wasm or something. And they'd call `.unwrap()` and don't want to write `.unwrap_unchecked()`. And the compiler could see that this can't be an error and therefore you won't need any error/format machinery. But with this, now the machinery is there and so the compiler would have to bring all the format machinery.
Josh: A lot of people care about this in embedded.
The 8472: In that case, you can just call `extend` instead of `write_all` method. `write_all` returns `Result` -- if you don't want that, just call the method that doesn't return `Result`.
The 8472: Either they want to check for out of memory or they don't -- and they have both options.
Josh: Your argument is anyone calling this who doesn't care about out-of-memory should call `extend_from`
The 8472: Or know it never fails, to use `let _ = write_all...`
Josh: For 93 releases of Rust, we've never made anybody think `write` would never error on `Vec`
The 8472: But we never promised we won't either. That's part of the contract.
Amanieu: Practically speaking, there's a lot of code that assumes this.
The 8472: That code is wrong. We've never set this as part of the API contract.
Amanieu: The `write!` macro returns a result that can't fail.
The 8472: But if you explicitly say that you don't care about the `Result` you can't ignore the errors.
Josh: This code exists. It is correct.
The 8472: Why are we writing the specification? People
Josh: I'm not saying we can make the change. But enough people depend on this so we can't change it overnight and have a release note saying they were always using it wrong. We can announce we'd like to change this, warn and deprecate this over time.
The 8472: Do you have examples / crater runs?
Amanieu: I do this.
The 8472: In unsafe code?
Amanieu: Not in unsafe but I use it for Command.
The 8472: That's a bug in your code.
Amanieu: It works, this is correct.
Josh: We have this argument many times. There are things in Rust that are not written down that people grew to rely on. And we don't blithely do this. We look at the crater runs, we provide lints.
Amanieu: Github code search for `let _ = write!(...)` has 33,000 matches. https://github.com/search?q=lang%3ARust+%22_+%3D+write%21%22&type=code
The 8472: This is also a pattern for files which can fail. They weren't making an assumption, they were just operating on good luck. That's not an argument that this would be a problem specifically for vec.
Amanieu: In a lot of the cases the target is a `String`.
Josh: There are 329 instances where the target is a variable called `v`. Which is likely a `Vec`
Josh: 2.9 thousand for writing to a variable named `s,` which is almost certainly a `String`.
Chris: Can you compare it to e.g. fd?
The 8472: What kind of crates are those?
Amanieu: I'm less concerned about the implementations for `Read`. You can do a `Write` and assume it has succeeded. You can't do that for `Read`.
Josh: There are 93 instances of that exact pattern in `rust-lang/rust` of `_ = write!`.
Amanieu: To be fair, most of these are `Strings`.
Josh: For some reason some is doing those to files.
The 8472: These are bugs.
Josh: The files ones are bugs.
The 8472: All of these are bugs.
Josh: We've never advertised indication that we'll never change this. People say this, we say it sometimes that this will never fail. Amanieu and I went to write a whole proposal of improving the formatting machinery that it will never fail.
Amanieu: In the implementation call the out of memory handler.
The 8472: Or you can use the fallible call and say `unwrap`.
...
Josh: There's also the topic of handling an out-of-memory error and allocating memory to try and recover from it and allocate memory
The 8472: I'm not saying people should handle the error. It's fine to turn it into a panic. The only problem is ignoring the result. I think your new API will be an improvement on that front. When we promise this is infallible, we'll have an explicit distinction between an infallible API that will panic and a fallible API that returns a `Result`.
Josh: I don't think anybody's disagreeing that there should be better error handling.
Chris: Can we ask what they're usecase is?
Amanieu: Kornelski's usecase is writing server applications that may run out of memory and trying to handle those. They initially used out-of-memory=panic. They're from Cloudflare and they want to use it in servers.
Josh: Makes sense.
The 8472: In some of those usecases you accept large inputs, you have enough memory, you but aren't able to stream it. And then rely on the allocator to tell you you don't have enough memory. At work we do a bunch of image processing and not everything can do stream processing and mostly individual requests are limited, but in theory with enough concurrent requestts stuff can run out of memory. Fallibility that handles large allocations is an improvement.
Josh: We could add a lint that tries to catch cases where people write to a `Vec` and ignore return values. That's going to have a lot of false positives. And many people will likely treat everything it says as false positive.
The 8472: But they could have just written `unwrap`, right?
Josh: They could, but they empirically didn't.
Amanieu: I think out-of-memory=panic is still the answer. We've removed that but I think we'll implement out-of-memory unwind hook.
Josh: If this lets us remove unwinding I'd be for it.
The 8472: With unwinding the HTTP server could return a 5xx error but having a `Result` would mean returning a better error and that would be an improvement over unwinding. You could do actual error handling there (even though it'd be best effort, sometimes your OOM process would still get killed and you'd get 5xx).
The 8472: Do you want to add an API guarantee that this will never return an error?
Amanieu: Yes!
Josh: Yes.
The 8472: And how do we let people write fallibly to a Vec?
Amanieu: `try_reserve`
Josh: Or having a type wrapper that could return possible `Result`.
The 8472: `try_reserve` could work for some usecases with but not all.
Amanieu: Yes, if you're copying a `Reader` to a `Writer`, you may not know how much to reserve.
The 8472: ??
Josh: I'd fine adding a fallible vec where all its methods are try methods. It doesn't have any methods that panic.
The 8472: We had an attempt at that, we had an RFC and we told people to go away.
Amanieu: We said if you want to write such a vec, it can exist as a crate outside of the library.
Josh: We also said we did not want to have the API surface duplication. But that's separate from accepting a type wrapper or similar that uses this approach. That's less of a duplication.
The 8472: When I complained about twice as many methods in constructors (like `Box::new`) this was ignored. We have all the `try_*` methods there.
Josh: We were pushing back on adding try to everything but we've added them piecemeal and the result is inconsistent.
The 8472: I wouldn't say adding try stuff on `Box` is piecemeal, it's adding the infallible bits.
Josh: I've seen people argue that the argument for `Box` is that it's the closest we have for `malloc`, but ??
The 8472: `Write` is by API-surface one of the fallible APIs. We're repurposing it for infallible use without offering an alternative.
Josh: I think we should add alternatives.
Chris: Would people accept ACP for fallible vec?
Josh: I'd sign off on that.
Amanieu: It doesn't work with our basic traits (`Clone`, `Extend` etc.)
The 8472: Also, every API that accepts Vec wouldn't accept this. Mixing usecases between fallible and infallible would be a problem.
Josh: Whatever method would turn a fallible vec into an infallible vec would have to come with a big warning.
Amanieu: I think we're basically at the ponit where Rust has committed to not handling memory errors.
The 8472: I disagree. There are a few places where you care about large allocations and those need fallible APIs. Maybe people will write safety critical code to handle these and keep going
Amanieu: I don't think we'll ever get to the Zig where every allocation is fallible and you have to handle it
The 8472: You should have at least the ability to handle the obvious cases where you e.g. accept untrusted input without panicking.
Amanieu: And that ability is `try_reserve`
The 8472: IO is one of the cases where you may have a large amount of data that you have to handle this.
Amanieu: Can we defer this to next week?
### (waiting on team) rust.tf/148265 *library: core: document layout guarantee of \`TypeId\`*
Deferred to t-compiler.
### (waiting on team) rust.tf/149045 *implement PartialEq\<Vec\<U\>\> for \[T; N\] and &\[T; N\]*
### (waiting on team) rust.tf/149408 *refactor: remove Ord bound from BinaryHeap::new etc*
FCP started
### (new change proposal) rust.tf/libs719 *Guarantee direct access to std{in,out,err} file descriptors as BorrowedFd\<'static\>*
Amanieu: Previous discussion was on exposing the taking file descriptor.
The 8472: I have a PR for that, but that's not the desire here. Taking gives you a FD with a different number. This is about ??
Chris: how is this different from the std in/out/err?
The 8472: They take locks. The raw ones are internal.
Josh: We made a change in the past to make these `'static`.
Amanieu: Ah, it's the `'static` lifetime. And we guarantee these are always open?
The 8472: If you pull a switcheroo via something that switches STDIN terminal with a nonblocking one and another thread is piping it into a file and fails. But that's a logic error.
Amanieu: stdin/out/err implements BorrowedFd. What if we had an inherent method that returned a BorrowedFd with the static lifetime?
The 8472: The question is more about whether we want to provide the guarantee.
Amanieu: You can create the STDIN and then call `fd` on it so it's already possible today. The guearantee exists today.
The 8472: If we're making this official, we'd be saying that it's fine to bypass the locks and you'd get tearing etc.
Josh: The thing you get access to is `BorrwedFd` but calling `write` on a file descriptor is if you don't pay attention to the locking you may get interleaving. That's not what we're blessing.
The 8472: What I'm proposing is taking `BorrowedFd` and directing it to a file.
Josh: But there are many other ways. Our locking is best-effort.
Josh: I don't think the existence of this proposed API prevents a program from closing stdin/out/err.
The 8742: What do you mean by closing? Calling the `close` syscall? That's unsound.
Josh: If you never call any methods that call stdin/out/err then it's sound, but it is a bad idea
The 8472: If you're doing something in a sufficiently controlled manner where you can write unsafe comments. But composable can't do this.
Josh: That's a great summary. I agree composable rust code needs this. I'm thinking of someone who's sandboxing things, making a special process that can only write to a specific file descriptor.
The 8472: Even in the sandboxing case I'd recommend setting stdin/out/err to some FD that uses something totally useless. E.g. a disconnected pipe.
Josh: Sure. I'm thinking of a case where you have a seccomp set up such that writing to the FD 0,1,2 means you no longer exist any more.
The 8472: I still think they should stay open though. I can't think of anything that improves when you close them. As far as the standard library is concerned, they're shared ambient resources that nobody owns so no one can close them.
The 8472: So I'm okay with the addition, but we should document the risks.
Amanieu: Sure but you can do it today by leaking it.
The 8472: It's not great but you can do this already, so yeah.
Josh: In the world in which it wasn't alerady possible, I could imagine proposing to acquire and then leak the stdout/stderr lock. But I'm not proposing that's what we should do here.
Amanieu: Good point. Why does this need a static lifetime?
The 8472: Signal handlers?
Amanieu: In that case we don't want this to be a method on STDIN. You probably want a more direct way to do this. But I'm not sure we can actually make it `const`. That works on Unix, but for windows you need to call `GetSTDHandle`.
The 8472: This isn't asking for anything on Windows.
Amanieu: I'd expect to provide borrowed handles for those.
The 8472: We can't. I've discussed this with Chris and the ipmlementation will have to leak the handles because it's a shared resource and we don't know when we can close it. The only way we can replace it is swapping the stdio pointer and leave the handle open.
Amanieu: So you have essentially a 'take and replace' rather than "set". It's unfortunate that if the user knows what they're doing, they still can't close it.
The 8472: We can document when and how to close it with unsafe if you call it in non-threaded code early in `main`.
The 8472: Do we implement s-handle for ..?
Amanieu: Yes, we do. It returns a valid BorrowedHandle with the value `null`
The 8472: Should we keep it that way? I was planning to return an Option of a handle.
Chris: I'd prefer an `Option`
Amanieu: I feel it might be too late.
The 8472: How do the other windows APis react when you pass a null pointer to them as handle?
Amanieu: NULL is always an invalid handle.
Chris: It's possible some APIs may interpret NULL as a special thing.
Amanieu: Is it too late to deprecate this? Probably.
The 8472: We can only do it for stdio.
Amanieu: Do you want to add `BorrowedHandle` to the wishlish we'd like to replace during edition switchover?
The 8472: Can we make the trait unavailable on an edition?
Amanieu: No because FromRawHandle is documented as "it may have a "
Josh: This ACP is asking for a std::os::fd API that returns three constant BorrowedFd values. I thikn it'd be good for us adding an equivalent BorrowedHandle on windows. I wonder if we could ask for that to be a separate ACP proposal.
Amanieu: Sure. But but the BorrowedHandles won't be able to be `const`
The 8472: We should check if we have any platforms that have file descriptors but not the standard POSIX input/outputs.
Amanieu: Ok. I this is fine. Let's accept it as it is.
Josh: Since they're in `std::os::fd` they're clearly file descriptors so they should just be called `STDIN/STDOUT/STDERR`
Amanieu: Should these be in core? ... `BorrowedFd` is not in `core`
The 8472: Do we allow cfgs inside `os`, right?
Amanieu: we do. The `fd` module has a bunch of weird cfgs.
Josh: `stdin()` unconditionally returns `STDIN` rathre than an error.
Amanieu: On this target you can have it such that it never returns anything.
The 8472: It's the whole `fd` module that's under `cfg` not any of its items.
The 8472: Actually, there's the `RawFd` type alias under some weird conditions.
Amanieu: Yes, those don't have `c_int` or have a different one.
The 8472: We should check all those targets.
Amanieu: Hermit is a unikernel and has a libc which provides file descriptors. And some sort of filesystem.
The 8472: Is the MotorOS libc or not?
Amanieu: (looking it up)
The 8472: The libc crate doesn't support MotorOS
Amanieu: it provides standard unix read/write options. It has standard input for fd=0. I'd expect any targets that don't support these to just `cfg` them out.
The 8472: Yeah, okay.
### (new change proposal) rust.tf/libs715 *ACP: AtomicPtr::null() to create a null\-initialized atomic pointer*
The 8472: We do have ti fro regular pointers.
Josh: Given that `AtomicPtr` was supposed to be nullable.
Amanieu: can we do this as an associated constant instead?
Amanieu: It has inner mutability. That might be an issue.
The 8472: I think a method is fine. We also have a method for regular pointers. We don't have `NULL` as a constant.
Amanieu: I don't know why not. I don't thin we supported it at a time?
Josh: I think `const` seems fine here and it would work as expected.
The 8472: It's a free function not a primitive type.
Amanieu: a const with inner mutability feels weird
The 8472: It's a constructor, a function sounds fine. It could sit next to `new` etc.
Amanieu: You could do `AtomicPtr::null`, do a `fetch_add` and have it change.
Josh: It would crash.
Amanieu: It wouldn't. But it's confusing to users.
The 8472: I think having a `null` as a function is perfectly fine. I don't really see a reason that we should have a constant.
Amanieu: Can we agree that we should have either a function or constant?
Josh: Yes.
The 8472: I agree we should have something but I prefer it to be a function.
Josh: I'd prefer a const but I think it's important to ship this
Chris: It's consistent with `ptr::null()`
Amanieu: We can switch this to a constant once we do that for `ptr::null()`
The room: agreed, let's approve the function here.
(the meeting ended here)
### (new change proposal) rust.tf/libs718 *ACP: Add saturating artihmetic for \`SystemTime\`*
### (new change proposal) rust.tf/libs717 *Add \`\[\_\]::shift\_{left,right}\`*
### (new change proposal) rust.tf/libs713 *ACP: Add float constants for min/max limits of consecutive integers that convert to unique floats*
### (new change proposal) rust.tf/libs712 *Add an \`fN::mul\_add\` variant that might round twice*
### (new change proposal) rust.tf/libs711 *Add \`mem::needs\_clone\`*
### (new change proposal) rust.tf/libs710 *\`BinaryHeap::from\_raw\_vec\`*
### (new change proposal) rust.tf/libs709 *Add \`str::copy\_from\_str\` and other mutating \`str\` methods*
### (new change proposal) rust.tf/libs706 *More arithmetic functions for Duration*
### (stalled change proposal) rust.tf/libs133 *Add fmt::Write to io::Write adapter*
### (stalled change proposal) rust.tf/libs111 *Restructure ptr\_metadata to minimal support*
### (stalled change proposal) rust.tf/libs438 *ACP: \`ForwardInit\<'a, T\>\` to complement \`MaybeUninit\<T\>\`*
### (stalled change proposal) rust.tf/libs322 *\`AssertThreadSafe\` (name TBD) – a more general API for lifting conservative \`!Send\` or \`!Sync\` implementations*
### (stalled change proposal) rust.tf/libs347 *Context reactor hook*
### (stalled change proposal) rust.tf/libs501 *ACP: Add floating point representation conversions*
### (stalled change proposal) rust.tf/libs523 *Add a deterministic constructor for \`RandomState\`*
### (stalled change proposal) rust.tf/libs395 *\`impl core::str::Pattern for \[&str; N\]\`*
### (stalled change proposal) rust.tf/libs204 *Integer Manipulation API*
### (stalled change proposal) rust.tf/libs371 *ACP: primitive numeric traits*
_Generated by [fully-automatic-rust-libs-team-triage-meeting-agenda-generator](https://github.com/rust-lang/libs-team/tree/main/tools/agenda-generator)_