Skip to content

Should getting a BorrowedHandle from Stdin, Stdout or Stderr return a Result on Windows? #90964

Closed
@ChrisDenton

Description

@ChrisDenton

On Windows, the standard library's function for getting a stdio handle looks like this:

pub fn get_handle(handle_id: c::DWORD) -> io::Result<c::HANDLE> {
    let handle = unsafe { c::GetStdHandle(handle_id) };
    if handle == c::INVALID_HANDLE_VALUE {
        Err(io::Error::last_os_error())
    } else if handle.is_null() {
        Err(io::Error::from_raw_os_error(c::ERROR_INVALID_HANDLE as i32))
    } else {
        Ok(handle)
    }
}

Note that it only returns a handle if one is set, otherwise it returns an error.

In contrast, the public AsRawHandle stdio implementation ignores errors returned by GetStdHandle and just uses the returned value, whatever that may be.

impl AsRawHandle for io::Stdin {
    fn as_raw_handle(&self) -> RawHandle {
        unsafe { c::GetStdHandle(c::STD_INPUT_HANDLE) as RawHandle }
    }
}
// ...and similar for `Stdout` and `Stdin`.

The Safe I/O RFC introduced new types for managing handles. The AsHandle trait is intended to be a drop in replacement for the old AsRawHandle trait but returns a BorrowedHandle instead of a RawHandle.

I personally don't think AsHandle should be implemented for stdio types. Instead a function with a signature similar to this should be implemented:

fn try_as_handle(&self) -> io::Result<BorrowedHandle<'_>>;

It would work similarly to the internal get_handle function in that it will return an error if there is no handle to return.


Reasons for a try_as_handle() function instead of implementing AsHandle on stdio types:

  • The error is surfaced at its origin.
  • It gives users the correct mental model when thinking about Windows' stdio (especially since it differs from Unix).
  • A BorrowedHandle should be what the name implies: a borrow of a handle. It should not be an error sentinel value (which may overlap with an actual handle value).

Reasons not to do this:

  • Using a null or INVALID_HANDLE_VALUE will probably lead to an error in any case so it's unclear how much of an issue this is in practice.
  • It makes it harder to update code if AsRawHandle and AsHandle aren't implemented for all the same types.
  • Given the above, is changing this really worth the trade-off?

See also: I/O Safety Tracking Issue (#87074) and a previous discussion on internals.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-error-handlingArea: Error handlingA-ioArea: `std::io`, `std::fs`, `std::net` and `std::path`C-discussionCategory: Discussion or questions that doesn't represent real issues.O-windowsOperating system: WindowsT-libsRelevant to the library team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions