std::io: migrate ReadBuf to BorrowedBuf/BorrowedCursor by nrc · Pull Request #97015 · rust-lang/rust (original) (raw)

Sorry for the delay in my answer, @nrc, and thank you for taking my input into consideration 🙏

lifetime names (and position)

buffer is such an overloaded term

Ah interesting; I was seeing buf as referring to the byte slice, but you use it to refer to the borrow of that byte slice, which is indeed just as legitimate. So I don't mind 'b then being the 'buf part, with 'a becoming 'data:

pub fn unfilled<'buf>(self: &'buf mut BorrowBuf<'data>) -> BorrowCursor<'buf, 'data>


[regarding the ordering of lifetime params]

I'm not sure I've come across it explicitly, is it a documented pattern?

Not that I know of, but having a choice of pattern be made consistently is useful, and this ordering is the most suitable one, since:

But just a very tiny nit, of course 🙂


clone or reborrow?

Is it common? Is it a convention we should encourage in std?

It's very uncommon indeed. But I think it being that uncommon plays a non-negligible role in its own demise, sort to speak: something is jargony if used rarely / only between the savy people. Things such as rust-lang/reference#788 or even this very recent post https://haibane-tenshi.github.io/rust-reborrowing hint at somewhere, in Rust, missing clearer documentation of reborrowing:

In the stdlib, there is one instance where this explicit reborrowing comes in play:

impl<'orig> Pin<&'orig mut T> { fn as_mut<'r>(self: &'r mut Pin<&'orig mut T>) -> Pin<&'r mut T>

and is indeed the reason for needing these pesky .as_mut()s calls when dealing with Pins.

See also:

So what I think would benefit the language would be for the Rust book, or some other documentation to be more forthcoming about the reborrowing of &muts, which is deeply related to the common beginner-ish surprise of: "implementing Iter is easy, but IterMut is not" (&'short mut &'long mut [T] only yields &'short mut [T] through reborrowing, whereas &'short &'long [T] can yield &'long [T] through Copy), which comes often on URLO / Discord.

With all that being said, if the word reborrow in and of itself, may be too scary for the stdlib to use, then I guess re-using Pin's as_mut() would at least lead to API consistency 🤷 [EDIT: duh, it's already being used, my bad]. The one word that worries me is clone, since people could end up surprised by the &mut receiver of it


owned or borrowed receivers

In a world with enough self-based methods in both APIs (and the then necessary .reborrow()ing methods), it would be possible to write the following helper function:

fn get_full_buffer<'data>(mut buf: BorrowBuf<'data>) -> &'data mut [u8] { buf.clear(); let mut cursor = buf.unfilled_mut(); // self-based / consumes buf cursor.ensure_init(); cursor.init_mut() // self-based }

With the current API, this function is impossible to write. To make it possible, some of the functions would have to become self-based. Hence my raising awareness about this point, and interest in trying to fix this, if possible, with some self-based methods.

That being said, I've been looking more in detail into how to adapt your API exactly for this purpose, and the only way to achieve it would be with a revamp changing the very point of BorrowCursor (that of not allowing it to modify some data of the BorrowBuf).


It constrains callees to having write-only access to the buffer.

I see 👍 in that case, we could consider exposing an extra getter to get that split: -> (&[u8], BorrowCursor…), à la https://doc.rust-lang.org/1.61.0/std/vec/struct.Vec.html#method.split_at_spare_mut:

fn split_at_unfilled<'buf>( self: &'buf mut BorrowBuf<'data>, ) -> (&'buf [u8], BorrowCursor<'buf, 'data>)


TL,DR

The API as it is now, seems fine to me ✅, although I'm still not 100% fond of the clone name for that reborrowing function.