-
Notifications
You must be signed in to change notification settings - Fork 170
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Prototype a new Buffer
trait.
#1290
base: main
Are you sure you want to change the base?
Conversation
I'm experimenting with a `Buffer` trait similar to #908, however I've run into a few problems. See the questions in examples/new_read.rs for details.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we go with this approach, I feel like there's a pretty good chance compiler folks will improve the error messages for us if we file bug reports. The story around passing in a mutable array but needing a slice instead has annoyed me for quite some time (I usually just as_mut_slice
everything). It was pretty confusing to figure out initially, so I'm sure better error messages would be welcome.
struct Wrapper<'a>(&'a mut [u8]); | ||
impl<'a> Wrapper<'a> { | ||
fn read(&mut self) { | ||
let _x: usize = read(stdin(), self.0).unwrap(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe this is due to rust-lang/rust#35919. You need to reborrow manually: &mut *self.0
.
One option is to make the Buffer trait impl owned types so the read
method borrows it. Unfortunately I believe this requires rust 1.65. Also it seems kinda ugly so maybe not worth it.
pub fn read<Fd: AsFd, Buf: Buffer<u8> + ?Sized>(
fd: Fd,
buf: &mut Buf,
) -> io::Result<Buf::Result<'_>> {
let len = backend::io::syscalls::read(fd.as_fd(), buf.as_maybe_uninitialized())?;
// SAFETY: `read` works.
unsafe { Ok(buf.finish(len)) }
}
pub trait Buffer<T> {
/// The result of the process operation.
type Result<'a>
where
T: 'a,
Self: 'a;
/// Convert this buffer into a maybe-unitiailized view.
fn as_maybe_uninitialized(&mut self) -> &mut [MaybeUninit<T>];
/// Convert a finished buffer pointer into its result.
///
/// # Safety
///
/// At least `len` bytes of the buffer must now be initialized.
unsafe fn finish(&mut self, len: usize) -> Self::Result<'_>;
}
/// Implements [`Buffer`] around the a slice of bytes.
///
/// `Result` is a `usize` indicating how many bytes were written.
impl<T> Buffer<T> for [T] {
type Result<'a>
= usize
where
T: 'a;
#[inline]
fn as_maybe_uninitialized(&mut self) -> &mut [MaybeUninit<T>] {
// SAFETY: This just casts away the knowledge that the elements are
// initialized.
unsafe { core::mem::transmute::<&mut [T], &mut [MaybeUninit<T>]>(self) }
}
#[inline]
unsafe fn finish(&mut self, len: usize) -> usize {
len
}
}
/// Implements [`Buffer`] around the a slice of uninitialized bytes.
///
/// `Result` is a pair of slices giving the initialized and uninitialized
/// subslices after the new data is written.
impl<T> Buffer<T> for [MaybeUninit<T>] {
type Result<'a>
= (&'a mut [T], &'a mut [MaybeUninit<T>])
where
T: 'a;
#[inline]
fn as_maybe_uninitialized(&mut self) -> &mut [MaybeUninit<T>] {
self
}
#[inline]
unsafe fn finish(&mut self, len: usize) -> Self::Result<'_> {
let (init, uninit) = self.split_at_mut(len);
// SAFETY: The user asserts that the slice is now initialized.
let init = slice::from_raw_parts_mut(init.as_mut_ptr().cast::<T>(), init.len());
(init, uninit)
}
}
/// Implements [`Buffer`] around the `Vec` type.
///
/// This implementation fills the buffer, overwriting any previous data, with
/// the new data data and sets the length.
#[cfg(feature = "alloc")]
impl<T> Buffer<T> for Vec<T> {
type Result<'a>
= usize
where
T: 'a;
#[inline]
fn as_maybe_uninitialized(&mut self) -> &mut [MaybeUninit<T>] {
self.clear();
self.spare_capacity_mut()
}
#[inline]
unsafe fn finish(&mut self, len: usize) -> usize {
self.set_len(len);
len
}
}
|
||
// Why does this get two error messages? | ||
let mut buf = [0, 0, 0]; | ||
let _x = read(stdin(), buf).unwrap(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Presumably because they're both valid. There's a hint assuming the type is an array and then the message that lists out all the valid types you can use.
use core::mem::MaybeUninit; | ||
use core::slice; | ||
|
||
/// A memory buffer that may be uninitialized. | ||
pub trait Buffer<T> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure how much it's worth discussing the implementation yet, but there are a few concerns:
- I don't think this trait should be public (yet)
as_maybe_uninitialized
is unsound in general, but not for our purposes. The conversion fromu8 -> MaybeUninit<u8>
is only valid if you never uninitialize theu8
. We don't do that since we're always passing this data to a syscall, but for a public trait (and probably for ourselves just in case), the conversion to maybeuninit needs to be unsafe.- I don't think we should have an implementation for arrays (
[T; N]
). Maybe there's value I'm missing, but it seems simple enough to require you to pass in a slice instead. - Same argument for
Vec<T>
, you should be required to pass in&mut Vec<T>
and it should usespare_capacity_mut
(but notclear
) plusset_len
. Though maybe there's an argument for having the owned and borrowed versions with the owned one doing theclear
.
match read_uninit(self.fd.as_fd(), self.buf).map(|(init, _)| init.len()) { | ||
todo!("FIXME: see \"Why doesn't this work?\" in examples/new_read.rs"); | ||
/* | ||
match read(self.fd.as_fd(), self.buf).map(|(init, _)| init.len()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
&mut *self.buf
or the GAT version of the Buffer trait fixes this, as per my other comment.
I'm experimenting with a
Buffer
trait similar to #908, however I've run into a few problems. See the questions in examples/new_read.rs for details.@notgull @SUPERCILEX