-
Notifications
You must be signed in to change notification settings - Fork 12.9k
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
Tracking Issue for io::Cursor::{split, split_mut}
#86369
Comments
One additional concern that @mystor pointed out is that stabilizing these APIs might prevent us from adding support to non-contiguous buffers/sets of buffers to We should be absolutely certain that we don't intend to extend |
Wouldn't that still work though? Since those contiguous buffers won't implement |
Oh, I had forgotten that
Sounds like a plan! |
I renamed fn remaining(&self) -> u64; Is that ok or should i remove it again? |
sounds great |
io::Cursor::{remaining, is_empty}
io::Cursor::{remaining, remaining_slice, is_empty}
Add `io::Cursor::{remaining, remaining_slice, is_empty}` Tracking issue: rust-lang#86369 I came across an inconvenience when answering the following [Stack Overflow](https://stackoverflow.com/questions/67831170) question. To get the remaining slice you have to call `buff.fill_buf().unwrap()`. Which in my opinion doesn't really tell you what is returned (in the context of Cursor). To improve readability and convenience when using Cursor i propose adding the method `remaining`. The next thing i found inconvenient (unnecessary long) was detecting if the cursor reached the end. There are a few ways this can be achieved right now: - `buff.fill_buf().unwrap().is_empty()` - `buff.position() >= buff.get_ref().len()` - `buff.bytes().next().is_none()` Which all seem a bit unintuitive, hidden in trait documentations or just a bit long for such a simple task. Therefor i propose another method called `is_empty`, maybe with another name, since this one may leave room for interpretation on what really is empty (the underlying slice, the remaining slice or maybe the position). Since it seemed easier to create this PR instead of an RFC i did that, if an RFC is wanted, i can close this PR and write an RFC first.
Add `io::Cursor::{remaining, remaining_slice, is_empty}` Tracking issue: rust-lang#86369 I came across an inconvenience when answering the following [Stack Overflow](https://stackoverflow.com/questions/67831170) question. To get the remaining slice you have to call `buff.fill_buf().unwrap()`. Which in my opinion doesn't really tell you what is returned (in the context of Cursor). To improve readability and convenience when using Cursor i propose adding the method `remaining`. The next thing i found inconvenient (unnecessary long) was detecting if the cursor reached the end. There are a few ways this can be achieved right now: - `buff.fill_buf().unwrap().is_empty()` - `buff.position() >= buff.get_ref().len()` - `buff.bytes().next().is_none()` Which all seem a bit unintuitive, hidden in trait documentations or just a bit long for such a simple task. Therefor i propose another method called `is_empty`, maybe with another name, since this one may leave room for interpretation on what really is empty (the underlying slice, the remaining slice or maybe the position). Since it seemed easier to create this PR instead of an RFC i did that, if an RFC is wanted, i can close this PR and write an RFC first.
I'll just note that this change conflicts with I've also noticed an inconvenience that won't affect stable: since the method is "unstable" on nightly, it triggers an error, even though the trait is in scope. |
That seems unfortunate. I'd be okay with changing the signature to return a About the error in nightly, I don't know how this is handled normally in std. But the impact should be rather small, since |
I don't mean an error in libstd or it's test suite. But rather the greater ecosystem, anywhere using |
I didn't either. I meant the use of |
Yea, I see two solutions (so far, maybe there's a better): either libstd changes to so that nightly doesn't include the conflict, or the ecosystem that depends on (I agree failing nightly tests is acceptable to a degree, but at the same time, if they're all set to allow failure, it's very easy to miss that nightly is broken, and if they block the PR, then it disrupts library developers. 🤷 ) |
@yaahc can |
Yes, to either option, whichever you prefer. I'm not confident the |
Remove unstable `io::Cursor::remaining` Adding `io::Cursor::remaining` in rust-lang#86037 caused a conflict with the implementation of `bytes::Buf` for `io::Cursor`, leading to an error in nightly, see rust-lang#86369 (comment). This fixes the error by temporarily removing the `remaining` function. r? `@yaahc`
It would be nice if Sometimes I want to avoid copying a large chunk of data and get a slice of the byte buffer instead. Let's say I've read a file into memory, and now I'm pulling out individual assets: #![feature(cursor_remaining)]
use std::io::{self, BufRead, Cursor, Read};
struct Asset<'file> {
data: &'file [u8],
}
impl<'file> Asset<'file> {
fn load(cursor: &mut Cursor<&'file [u8]>) -> io::Result<Self> {
let mut len = [0u8; 2];
cursor.read_exact(&mut len[..])?;
let len = u16::from_le_bytes(len) as usize;
let data = cursor.remaining_slice().get(..len)
.ok_or(io::ErrorKind::UnexpectedEof)?;
cursor.consume(len);
Ok(Self { data })
}
} This fails to compile because the slice from
|
io::Cursor::{remaining, remaining_slice, is_empty}
io::Cursor::{remaining_slice, is_empty}
That would be nice, but sadly i see no way to do this. I tried to annotate all lifetimes that exist when impl<T: AsRef<[u8]>> Cursor<&'a [u8]> {
pub fn remaining_slice<'b>(&'b self) -> &'b [u8] {
let slice: &'b &'a [u8] = AsRef::as_ref(&'b self.inner);
let slice: &'a [u8] = *slice;
// where 'a: 'b
slice
}
} It is not possible to change the returned lifetime to |
@yaahc do you think we can start an FCP? Its been about 3 month since the initial PR. |
sure. @rfcbot merge |
Team member @yaahc has proposed to merge this. The next step is review by the rest of the tagged team members: Concerns: Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! See this document for info about what commands tagged team members can give me. |
Hmmm, so I have a couple small thoughts here:
If we went with (2), I like the name |
My initial PR was with What about adding fn remaining(&self) -> usize; |
I personally think |
@rfcbot concern is_empty As pointed out above, the meaning of #![feature(cursor_remaining)]
use std::io::Write;
fn main() {
let mut buf = [0u8; 10];
let mut cursor = std::io::Cursor::new(&mut buf[..]);
dbg!(cursor.is_empty()); // false
cursor.write_all(b"1234567").unwrap();
dbg!(cursor.is_empty()); // false
cursor.write_all(b"890").unwrap();
dbg!(cursor.is_empty()); // true
} Here, |
This API seems focussed on the |
Note that the length of the underlying buffer doesn't change when reading or writing or seeking through it. The only thing that changes is the position of the cursor, and the bytes in the buffer (when writing). (E.g. seeking to the beginning will 'unempty' it and produce the already read data again.) |
I really like that, looking at the cursor symmetrically. You're talking about something like this? pub struct Cursor<T> {
pub fn remaining(&self) -> &[u8];
pub fn remaining_mut(&mut self) -> &mut [u8];
pub fn preceding(&self) -> &[u8];
pub fn preceding_mut(&mut self) -> &mut [u8];
pub fn split_at(&self) -> (&[u8], &[u8]);
pub fn split_at_mut(&mut self) -> (&mut [u8], &mut [u8]);
} |
It doesn't look like this will go through in its current form, so I'm cancelling the proposed FCP. @rfcbot cancel @jkugelman Yeah, something like that. Or |
@m-ou-se proposal cancelled. |
Stumbled upon these functions today. Considering the concerns, would it be worth removing the current implementation? |
I once made a commit with possible alternatives but wasn't sure about if the implementation should not be generic over My Implementation was based on these functions: fn split(&self) -> (&[u8], &[u8]);
fn before(&self) -> &[u8];
fn after(&self) -> &[u8];
fn split_mut(&mut self) -> (&mut [u8], &mut [u8]);
fn before_mut(&mut self) -> &mut [u8];
fn after_mut(&mut self) -> &mut [u8]; Commit: soerenmeier@eab83e3 |
io::Cursor::{remaining_slice, is_empty}
io::Cursor::{split, split_mut}
Replace `io::Cursor::{remaining_slice, is_empty}` This is a late follow up to the concerns raised in rust-lang#86369. rust-lang#86369 (comment) > This API seems focussed on the `Read` side of things. When `Seek`ing around and `Write`ing data, `is_empty` becomes confusing and `remaining_slice` is not very useful. When writing, the part of the slice before the cursor is much more interesting. Maybe we should have functions for both? Or a single function that returns both slices? (If we also have a `mut` version, a single function would be useful to allow mutable access to both sides at once.) New feature name: `cursor_remaining` > `cursor_split`. Added functions: ```rust fn split(&self) -> (&[u8], &[u8]); // fn before(&self) -> &[u8]; // fn after(&self) -> &[u8]; fn split_mut(&mut self) -> (&mut [u8], &mut [u8]); // fn before_mut(&mut self) -> &mut [u8]; // fn after_mut(&mut self) -> &mut [u8]; ``` A question was raised in rust-lang#86369 (comment) about whether to return a lifetime that would reflect the lifetime of the underlying bytes (`impl Cursor<&'a [u8]> { fn after(&self) -> &'a [u8] }`). The downside of doing this would be that it would not be possible to implement these functions generically over `T: AsRef<[u8]>`. ## Update Based on the review, before* and after* methods where removed.
Replace `io::Cursor::{remaining_slice, is_empty}` This is a late follow up to the concerns raised in rust-lang#86369. rust-lang#86369 (comment) > This API seems focussed on the `Read` side of things. When `Seek`ing around and `Write`ing data, `is_empty` becomes confusing and `remaining_slice` is not very useful. When writing, the part of the slice before the cursor is much more interesting. Maybe we should have functions for both? Or a single function that returns both slices? (If we also have a `mut` version, a single function would be useful to allow mutable access to both sides at once.) New feature name: `cursor_remaining` > `cursor_split`. Added functions: ```rust fn split(&self) -> (&[u8], &[u8]); // fn before(&self) -> &[u8]; // fn after(&self) -> &[u8]; fn split_mut(&mut self) -> (&mut [u8], &mut [u8]); // fn before_mut(&mut self) -> &mut [u8]; // fn after_mut(&mut self) -> &mut [u8]; ``` A question was raised in rust-lang#86369 (comment) about whether to return a lifetime that would reflect the lifetime of the underlying bytes (`impl Cursor<&'a [u8]> { fn after(&self) -> &'a [u8] }`). The downside of doing this would be that it would not be possible to implement these functions generically over `T: AsRef<[u8]>`. ## Update Based on the review, before* and after* methods where removed.
Rollup merge of rust-lang#109174 - soerenmeier:cursor_fns, r=dtolnay Replace `io::Cursor::{remaining_slice, is_empty}` This is a late follow up to the concerns raised in rust-lang#86369. rust-lang#86369 (comment) > This API seems focussed on the `Read` side of things. When `Seek`ing around and `Write`ing data, `is_empty` becomes confusing and `remaining_slice` is not very useful. When writing, the part of the slice before the cursor is much more interesting. Maybe we should have functions for both? Or a single function that returns both slices? (If we also have a `mut` version, a single function would be useful to allow mutable access to both sides at once.) New feature name: `cursor_remaining` > `cursor_split`. Added functions: ```rust fn split(&self) -> (&[u8], &[u8]); // fn before(&self) -> &[u8]; // fn after(&self) -> &[u8]; fn split_mut(&mut self) -> (&mut [u8], &mut [u8]); // fn before_mut(&mut self) -> &mut [u8]; // fn after_mut(&mut self) -> &mut [u8]; ``` A question was raised in rust-lang#86369 (comment) about whether to return a lifetime that would reflect the lifetime of the underlying bytes (`impl Cursor<&'a [u8]> { fn after(&self) -> &'a [u8] }`). The downside of doing this would be that it would not be possible to implement these functions generically over `T: AsRef<[u8]>`. ## Update Based on the review, before* and after* methods where removed.
Feature gate:
#![feature(cursor_split)]
Steps / History
io::Cursor::{remaining, remaining_slice, is_empty}
#86037io::Cursor::{remaining_slice, is_empty}
#109174Unresolved Questions/Issues
nameremaining
Addio::Cursor::{remaining, remaining_slice, is_empty}
#86037 (comment)change signature ofremaining
to returnusize
. Tracking Issue forio::Cursor::{split, split_mut}
#86369 (comment)conflict with<Cursor as bytes::Buf>::remaining
Tracking Issue forio::Cursor::{split, split_mut}
#86369 (comment)The text was updated successfully, but these errors were encountered: