Skip to content

as_slice/into_slice for IoSlice/IoSliceMut #93

Closed
@mpdn

Description

@mpdn

Proposal

Add an as_slice on IoSlice to go from &IoSlice<'a> to &'a [u8].
Add an into_slice on IoSliceMut to go from IoSliceMut<'a> to &'a mut [u8].

Problem statement

Using vectored IO (eg. write_vectored) is close to impossible to do correctly due to the difficulty of slicing/advancing IoSlice

Consider something like this:

fn test(mut out: impl Write) -> std::io::Result<()> {
    let data1 = [1; 8];
    let data2 = [15; 8];
    let io_slice1 = IoSlice::new(&data1);
    let io_slice2 = IoSlice::new(&data2);
    out.write_vectored(&[io_slice1, io_slice2])?;
    Ok(())
}

The issue here is that write_vectored may only do a partial write of a slices. To ensure we write all data we need to advance the slices. Dealing with this is a little bit more annoying than ordinary writes, but we might try something like this:

fn test(mut out: impl Write) -> std::io::Result<()> {
    let data1 = [1; 8];
    let data2 = [15; 8];
    let io_slice1 = IoSlice::new(&data1);
    let io_slice2 = IoSlice::new(&data2);

    let mut buf = [io_slice1, io_slice2];
    let mut buf = &mut buf[..];
    let mut written = 0;
    
    while !buf.is_empty() {
        if buf[0].len() < written {
            written -= buf[0].len();
            buf = &mut buf[1..];
        } else {
            buf[0] = IoSlice::new(&buf[0][written..]);
            written = out.write_vectored(buf)?;
        }
    }

    Ok(())
}

But this fails to compile! The problem is that in IoSlice::new(&buf[0][written..]) we try to slice the IoSlice. This uses the Deref trait implemented on the IoSlice, but this ends up borrowing the IO slice. Thus we cannot modify buf as we are also borrowing data stored in it. Instead, if we simply had an as_slice as above, we could simply write that as IoSlice::new(&buf[0].as_slice()[written..]) instead.

There has been a few similar solutions to this problem, such as rust-lang/rust#62726 and rust-lang/rust#70436. These have sort of stalled, in part due to the resulting signatures ending up being sort of odd in cases where we want to advance a slice of IoSlices. I think the above proposal is simpler and more basic as it allows the user to construct functions like write_all_vectored, advance, or advance_slices in ordinary safe Rust.

Motivation, use-cases

None found - I haven't actually been able to find any applications doing vectored IO in Rust. Only vectored IO I have found has been wrapping write_vectored and the like: https://github.com/search?l=Rust&p=2&q=%22write_vectored%22&type=Code

Solution sketches

Already have it implemented here: https://github.com/rust-lang/rust/compare/master...mpdn:rust:master?expand=1

Links and related work

Metadata

Metadata

Assignees

No one assigned

    Labels

    ACP-acceptedAPI Change Proposal is accepted (seconded with no objections)T-libs-apiapi-change-proposalA proposal to add or alter unstable APIs in the standard libraries

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions