You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I'm personally no longer sold on the infrastructure that we've got here I think. I was initially hoping that we'd have some safe way of working with these APIs and such, but in the end it all turned up unsafe. In light of all that I'd personally advocate for much simpler API:
@sfackler do you recall if we have a tracking issue for the alternative method of implementing this where we hint to the compiler that a buffer shouldn't be considered as undefined? (the strategy we discussed at the last work week)
There's a bunch of discussion in the PR adding it: #58363.
It turns out that we explicitly decided to not go in that direction in the lead up to 1.0, so I think it's worth an RFC that I haven't gotten around to writing. In particular, that approach can change behavior for user-provided Read implementations that are passed to e.g. io::copy even before it's stable.
I second the proposal for &mut [MaybeUninit<u8>] especially since the API design with MaybeUninit seems to be going in that direction anyway; there's just a few methods that I think need to be added to make this work nicely:
// std::memimpl<T>MaybeUninit<T>{pubfninit_all(uninit:&mut[MaybeUninit<T>],val:T) -> &mut[T]whereT:Copy{// `T` could even be `Clone` since this method is panic-safe except for leaks
...// write `val` to all elementsSelf::as_init_all(uninit)}pubfninit_copy(uninit:&mut[MaybeUninit<T>],data:&[T]) -> &mut[T]whereT:Copy{// `T` could also be `Clone`assert_eq!(uninit.len(), data.len());unsafe{Self::as_first_ptr(uninit).copy(data, uninit.len());}Self::as_init_all(uninit)}pubunsafefnas_init_all(uninit:&mut[MaybeUninit<T>] -> &mut[T]{
...// assume-initialized intrinsicmem::transmute(uninit)}}// std::iopubtraitRead{// ... existing methodsfnread_uninit(&mutself,uninit:&mut[MaybeUninit<u8>]) -> io::Result<()>{self.read(MaybeUninit::init_all(uninit,0))}}
Then implementations that don't read the buffer can override read_uninit() and use MaybeUninit::as_first_ptr() to pass it to a syscall or copy to it from another buffer with init_copy().
Reacted by Jimmy Smith, Kornel, Icelk, Max Guppy and Filipe Rodrigues
The reason for it returning Result<&'a mut [u8]> is to allow using this in safe code as well as preventing malicious implementations - if Result<usize> was returned, as it is a case for other Read methods, an implementation could just say Ok(42) and watch the world burn when trying to interpret the uninitialized bytes. It is the responsibility of an implementation to ensure the mutable slice it returns is initialized.
The choice of 0 assignment is somewhat arbitrary. It can be some other constant or frozen uninitialized memory, as long it doesn't outright cause UB it's fine.
Note that such an implementation doesn't ensure that that result.as_ptr() == MaybeUninit::first_ptr(buf). Unsafe implementations that depend on this should check for this manually. This is necessary, as implementations could return values like Box::leak(vec![1, 2, 3].into_boxed_slice()) otherwise, which isn't verified by the compiler. Optimizer should be able to remove this assertion most of the time.
Reacted by Thomas de Zeeuw, Martin Habovštiak, Kornel, zopsicle and Filipe Rodrigues
The default implementation will make that pretty hard to use in practice, unless you already know that your Read implementation overrides it. You're commonly calling read with a large buffer and don't expect it to be entirely filled, so writing to the entire thing before the raw read call imposes a very large performance penalty. There's an issue somewhere on Tokio's issue tracker about a similar issue with their AsyncRead::read_buf method.
Activity
alexcrichton commentedon Aug 29, 2017
I'm personally no longer sold on the infrastructure that we've got here I think. I was initially hoping that we'd have some safe way of working with these APIs and such, but in the end it all turned up unsafe. In light of all that I'd personally advocate for much simpler API:
I don't think the complexity we have today is worth the cost currently, and I'd imagine that we could start making abstractions like:
tbu- commentedon Dec 13, 2017
I wrote a tiny library for reading into uninitialized buffers: https://crates.io/crates/buffer/
Maybe its ideas could be adapted for the standard library. The following is safe code using the library:
Adoption of something like this in the standard library would probably also help with adoption by third party crates, see e.g. tokio-rs/mio#628.
seanmonstar commentedon Feb 21, 2018
An alternative to the current API is to go along the path that
TrustedLendid, which is just to have an unsafe trait.With specialization landing, anyone who cares to can take advantage of
ReadUinit:sfackler commentedon Feb 21, 2018
That's incompatible with trait objects which are used pretty heavily with Read in particular.
seanmonstar commentedon Feb 21, 2018
Specialization is incompatible with trait objects?
sfackler commentedon Feb 21, 2018
If I have a
&mut Read, I have no way of checking if it implementsReadUninitor not.jethrogb commentedon Jan 28, 2019
@RalfJung suggested a function that takes
&mut [MaybeUninit<u8>]alexcrichton commentedon Apr 10, 2019
@sfackler do you recall if we have a tracking issue for the alternative method of implementing this where we hint to the compiler that a buffer shouldn't be considered as undefined? (the strategy we discussed at the last work week)
sfackler commentedon Apr 10, 2019
There's a bunch of discussion in the PR adding it: #58363.
It turns out that we explicitly decided to not go in that direction in the lead up to 1.0, so I think it's worth an RFC that I haven't gotten around to writing. In particular, that approach can change behavior for user-provided Read implementations that are passed to e.g.
io::copyeven before it's stable.abonander commentedon Jul 10, 2019
I second the proposal for
&mut [MaybeUninit<u8>]especially since the API design withMaybeUninitseems to be going in that direction anyway; there's just a few methods that I think need to be added to make this work nicely:Then implementations that don't read the buffer can override
read_uninit()and useMaybeUninit::as_first_ptr()to pass it to a syscall or copy to it from another buffer withinit_copy().sugar700 commentedon Jul 31, 2019
I wonder about having a method like the following in
Read.The reason for it returning
Result<&'a mut [u8]>is to allow using this in safe code as well as preventing malicious implementations - ifResult<usize>was returned, as it is a case for otherReadmethods, an implementation could just sayOk(42)and watch the world burn when trying to interpret the uninitialized bytes. It is the responsibility of an implementation to ensure the mutable slice it returns is initialized.The choice of
0assignment is somewhat arbitrary. It can be some other constant or frozen uninitialized memory, as long it doesn't outright cause UB it's fine.Note that such an implementation doesn't ensure that that
result.as_ptr() == MaybeUninit::first_ptr(buf). Unsafe implementations that depend on this should check for this manually. This is necessary, as implementations could return values likeBox::leak(vec![1, 2, 3].into_boxed_slice())otherwise, which isn't verified by the compiler. Optimizer should be able to remove this assertion most of the time.sfackler commentedon Jul 31, 2019
The default implementation will make that pretty hard to use in practice, unless you already know that your Read implementation overrides it. You're commonly calling read with a large buffer and don't expect it to be entirely filled, so writing to the entire thing before the raw read call imposes a very large performance penalty. There's an issue somewhere on Tokio's issue tracker about a similar issue with their AsyncRead::read_buf method.
7 remaining items
Rollup merge of rust-lang#64123 - danielhenrymantilla:add_comment_abo…
Rollup merge of rust-lang#64123 - danielhenrymantilla:add_comment_abo…
Rollup merge of rust-lang#76242 - RalfJung:read-fixme, r=Dylan-DPC
Kestrer commentedon Jan 10, 2021
Can this be closed now that the reading into unintialized buffers RFC is merged and #78485 exists? Since the API is going to be removed anyway, it seems pointless to keep this open.
sfackler commentedon Jan 10, 2021
It will be closed when the API is removed.
Remove Initializer, unsafe feature, compiles
Auto merge of rust-lang#81156 - DrMeepster:read_buf, r=joshtriplett