Skip to content
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

Update for io_safety being stabilized. #41

Merged
merged 8 commits into from
Aug 16, 2022
29 changes: 4 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,37 +95,16 @@ is what motivates having `BorrowedFd` instead of just using `&OwnedFd`.
Note the use of `Option<OwnedFd>` as the return value of `open`, representing
the fact that it can either succeed or fail.

## I/O Safety in Rust Nightly
## I/O Safety in Rust

The I/O Safety
[implementation PR](https://github.com/rust-lang/rust/pull/87329) has now
landed and is available on Rust Nightly. It can be used directly, or through
io-lifetimes: when `io_lifetimes_use_std` mode is enabled, io-lifetimes uses
the std's `OwnedFd`, `BorrowedFd`, and `AsFd` instead of defining its own.

To enable `io_lifetimes_use_std` mode:
- Set the environment variable `RUSTFLAGS=--cfg=io_lifetimes_use_std`, and
- add `#![cfg_attr(io_lifetimes_use_std, feature(io_safety))]` to your
lib.rs or main.rs.

Note that, unfortunately, `io_lifetimes_use_std` mode doesn't support the
optional impls for third-party crates.

The code in `std` uses `From<OwnedFd>` and `Into<OwnedFd>` instead of `FromFd`
and `IntoFd`. io-lifetimes is unable to provide impls for these for third-party
types, so it continues to provide `FromFd` and `IntoFd` for now, with default
impls that forward to `From<OwnedFd>` and `Into<OwnedFd>` in
`io_lifetimes_use_std` mode.
I/O Safety feature is stablized in Rust 1.63. With this version or later,
io-lifetimes will use and re-export the standard-library types and traits. With
older versions, io-lifetimes defines its own copy of these types and traits.

io-lifetimes also includes several features which are not (yet?) in std,
including the portability traits `AsFilelike`/`AsSocketlike`/etc., the
`from_into_*` functions in the `From*` traits, and [views].

If you test a crate with the std I/O safety types and traits, or io-lifetimes
in `io_lifetimes_use_std` mode, please post a note about it in the
[I/O safety tracking issue] as an example of usage.

[I/O safety tracking issue]: https://github.com/rust-lang/rust/issues/87074
[views]: https://docs.rs/io-lifetimes/*/io_lifetimes/views/index.html

## Prior Art
Expand Down
38 changes: 38 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ use std::env::var;
use std::io::Write;

fn main() {
// I/O safety is stabilized in Rust 1.63.
if has_io_safety() {
use_feature("io_lifetimes_use_std")
}

// Niche optimizations for `Borrowed*` and `Owned*` depend on `rustc_attrs`
// which, outside of `std`, are only available on nightly.
use_feature_or_nothing("rustc_attrs");
Expand Down Expand Up @@ -65,3 +70,36 @@ fn has_panic_in_const_fn() -> bool {

child.wait().unwrap().success()
}

/// Test whether the rustc at `var("RUSTC")` supports the I/O safety feature.
fn has_io_safety() -> bool {
let out_dir = var("OUT_DIR").unwrap();
let rustc = var("RUSTC").unwrap();

let mut child = std::process::Command::new(rustc)
.arg("--crate-type=rlib") // Don't require `main`.
.arg("--emit=metadata") // Do as little as possible but still parse.
.arg("--out-dir")
.arg(out_dir) // Put the output somewhere inconsequential.
.arg("-") // Read from stdin.
.stdin(std::process::Stdio::piped()) // Stdin is a pipe.
.spawn()
.unwrap();

writeln!(
child.stdin.take().unwrap(),
"\
#[cfg(unix)]\n\
use std::os::unix::io::OwnedFd as Owned;\n\
#[cfg(target_os = \"wasi\")]\n\
use std::os::wasi::io::OwnedFd as Owned;\n\
#[cfg(windows)]\n\
use std::os::windows::io::OwnedHandle as Owned;\n\
\n\
pub type Success = Owned;\n\
"
)
.unwrap();

child.wait().unwrap().success()
}
2 changes: 0 additions & 2 deletions examples/easy-conversions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
//! implementing `IntoFilelike` and `FromSocketlike` to types implementing
//! `FromFilelike` and `IntoSocketlike`, respectively.

#![cfg_attr(io_lifetimes_use_std, feature(io_safety))]

use io_lifetimes::FromFilelike;
use std::fs::File;
use std::io::{self, Read};
Expand Down
20 changes: 9 additions & 11 deletions examples/flexible-apis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@
//! The following uses the POSIX-ish `Fd` types; similar considerations
//! apply to the Windows and portable types.

#![cfg_attr(io_lifetimes_use_std, feature(io_safety))]

#[cfg(all(feature = "close", not(windows)))]
use io_lifetimes::{AsFd, BorrowedFd, IntoFd, OwnedFd};
use io_lifetimes::{AsFd, BorrowedFd, OwnedFd};

/// The simplest way to accept a borrowed I/O resource is to simply use a
/// `BorrwedFd` as an argument. This doesn't require the function to have any
Expand Down Expand Up @@ -53,14 +51,14 @@ fn consume_fd_a(fd: OwnedFd) {
/// has the advantage of allowing users to pass in any type implementing
/// `IntoFd` directly.
#[cfg(all(feature = "close", not(windows)))]
fn consume_fd_b<Fd: IntoFd>(fd: Fd) {
let _ = fd.into_fd();
fn consume_fd_b<Fd: Into<OwnedFd>>(fd: Fd) {
let _: OwnedFd = fd.into();
}

/// Another way to do this is to use an `impl IntoFd` parameter.
#[cfg(all(feature = "close", not(windows)))]
fn consume_fd_c(fd: impl IntoFd) {
let _ = fd.into_fd();
fn consume_fd_c(fd: impl Into<OwnedFd>) {
let _: OwnedFd = fd.into();
}

/// Now let's see how the APIs look for users.
Expand All @@ -87,13 +85,13 @@ fn main() {
let b = std::fs::File::open("Cargo.toml").unwrap();
let c = std::fs::File::open("Cargo.toml").unwrap();

// The simple option requires an `.into_fd()` at the callsite.
consume_fd_a(a.into_fd());
// The simple option requires an `.into()` at the callsite.
consume_fd_a(a.into());

// Another option can take any `IntoFd` type directly.
// Another option can take any `Into<OwnedFd>` type directly.
consume_fd_b(b);

// The other option can take any `IntoFd` type directly.
// The other option can take any `Into<OwnedFd>` type directly.
consume_fd_c(c);
}

Expand Down
7 changes: 3 additions & 4 deletions examples/hello.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
//! the io-lifetimes API.

#![cfg_attr(not(rustc_attrs), allow(unused_imports))]
#![cfg_attr(io_lifetimes_use_std, feature(io_safety))]

#[cfg(feature = "close")]
use io_lifetimes::example_ffi::*;
Expand All @@ -13,7 +12,7 @@ use std::{
};

#[cfg(all(unix, feature = "close"))]
use io_lifetimes::{AsFd, FromFd, OwnedFd};
use io_lifetimes::{AsFd, OwnedFd};

#[cfg(windows)]
use io_lifetimes::{AsHandle, FromHandle, OwnedHandle};
Expand All @@ -40,7 +39,7 @@ fn main() -> io::Result<()> {
};

// Convert into a `File`. No `unsafe` here!
let mut file = File::from_fd(fd);
let mut file = File::from(fd);
writeln!(&mut file, "greetings, y'all")?;

// We can borrow a `BorrowedFd` from a `File`.
Expand Down Expand Up @@ -95,7 +94,7 @@ fn main() -> io::Result<()> {
};

// Convert into a `File`. No `unsafe` here!
let mut file = File::from_handle(handle);
let mut file = File::from(handle);
writeln!(&mut file, "greetings, y'all")?;

// We can borrow a `BorrowedHandle` from a `File`.
Expand Down
30 changes: 20 additions & 10 deletions examples/owning-wrapper.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
//! A simple example implementing the main traits for a type.

#![cfg_attr(io_lifetimes_use_std, feature(io_safety))]

#[cfg(not(windows))]
#[cfg(any(feature = "close", not(io_lifetimes_use_std)))]
use io_lifetimes::FromFd;
#[cfg(windows)]
#[cfg(any(feature = "close", not(io_lifetimes_use_std)))]
use io_lifetimes::FromHandle;
#[cfg(not(windows))]
#[cfg(not(io_lifetimes_use_std))]
use io_lifetimes::IntoFd;
#[cfg(windows)]
#[cfg(not(io_lifetimes_use_std))]
use io_lifetimes::IntoHandle;
use io_lifetimes::OwnedFilelike;
#[cfg(not(windows))]
use io_lifetimes::{AsFd, BorrowedFd, FromFd, IntoFd, OwnedFd};
use io_lifetimes::{AsFd, BorrowedFd, OwnedFd};
#[cfg(windows)]
use io_lifetimes::{AsHandle, BorrowedHandle, FromHandle, IntoHandle, OwnedHandle};
use io_lifetimes::{AsHandle, BorrowedHandle, OwnedHandle};

/// A wrapper around a file descriptor.
///
Expand All @@ -33,6 +43,7 @@ impl AsFd for Thing {
}
}

#[cfg(not(io_lifetimes_use_std))]
#[cfg(not(windows))]
impl IntoFd for Thing {
#[inline]
Expand All @@ -41,7 +52,6 @@ impl IntoFd for Thing {
}
}

#[cfg(not(io_lifetimes_use_std))]
#[cfg(not(windows))]
impl From<Thing> for OwnedFd {
#[inline]
Expand All @@ -50,6 +60,7 @@ impl From<Thing> for OwnedFd {
}
}

#[cfg(not(io_lifetimes_use_std))]
#[cfg(not(windows))]
impl FromFd for Thing {
#[inline]
Expand All @@ -58,7 +69,6 @@ impl FromFd for Thing {
}
}

#[cfg(not(io_lifetimes_use_std))]
#[cfg(not(windows))]
impl From<OwnedFd> for Thing {
#[inline]
Expand All @@ -75,6 +85,7 @@ impl AsHandle for Thing {
}
}

#[cfg(not(io_lifetimes_use_std))]
#[cfg(windows)]
impl IntoHandle for Thing {
#[inline]
Expand All @@ -83,7 +94,6 @@ impl IntoHandle for Thing {
}
}

#[cfg(not(io_lifetimes_use_std))]
#[cfg(windows)]
impl From<Thing> for OwnedHandle {
#[inline]
Expand All @@ -92,6 +102,7 @@ impl From<Thing> for OwnedHandle {
}
}

#[cfg(not(io_lifetimes_use_std))]
#[cfg(windows)]
impl FromHandle for Thing {
#[inline]
Expand All @@ -100,7 +111,6 @@ impl FromHandle for Thing {
}
}

#[cfg(not(io_lifetimes_use_std))]
#[cfg(windows)]
impl From<OwnedHandle> for Thing {
#[inline]
Expand All @@ -119,7 +129,7 @@ fn main() {
let file = std::fs::File::open("Cargo.toml").unwrap();
let thing = Thing::from_into_fd(file);
let _ = thing.as_fd();
let _ = thing.into_fd();
let _: OwnedFd = thing.into();
}

// Minimally exercise `Thing`'s Windows API.
Expand All @@ -128,7 +138,7 @@ fn main() {
let file = std::fs::File::open("Cargo.toml").unwrap();
let thing = Thing::from_into_handle(file);
let _ = thing.as_handle();
let _ = thing.into_handle();
let _: OwnedHandle = thing.into();
}

// Implementing the above traits makes the blanket impls for the portable
Expand Down
2 changes: 0 additions & 2 deletions examples/portable-views.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
//! io-lifetimes provides safe, convenient, and portable ways to temporarily
//! view an I/O resource as a `File`, `Socket`, or other types.

#![cfg_attr(io_lifetimes_use_std, feature(io_safety))]

use io_lifetimes::AsFilelike;
use std::fs::File;
use std::io::{self, stdout};
Expand Down
9 changes: 8 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
#![deny(missing_docs)]
#![cfg_attr(rustc_attrs, feature(rustc_attrs))]
#![cfg_attr(all(io_lifetimes_use_std, target_os = "wasi"), feature(wasi_ext))]
#![cfg_attr(io_lifetimes_use_std, feature(io_safety))]

mod portability;
mod traits;
Expand All @@ -48,8 +47,10 @@ pub use traits::AsFd;
#[cfg(windows)]
pub use traits::{AsHandle, AsSocket};
#[cfg(any(unix, target_os = "wasi"))]
#[allow(deprecated)]
pub use traits::{FromFd, IntoFd};
#[cfg(windows)]
#[allow(deprecated)]
pub use traits::{FromHandle, FromSocket, IntoHandle, IntoSocket};

#[cfg(not(io_lifetimes_use_std))]
Expand Down Expand Up @@ -88,6 +89,7 @@ pub use std::os::windows::io::{
// `From`/`Into`,
#[cfg(io_lifetimes_use_std)]
#[cfg(any(unix, target_os = "wasi"))]
#[allow(deprecated)]
impl<T: From<OwnedFd>> FromFd for T {
#[inline]
fn from_fd(owned_fd: OwnedFd) -> Self {
Expand All @@ -96,6 +98,7 @@ impl<T: From<OwnedFd>> FromFd for T {
}
#[cfg(io_lifetimes_use_std)]
#[cfg(any(unix, target_os = "wasi"))]
#[allow(deprecated)]
impl<T> IntoFd for T
where
OwnedFd: From<T>,
Expand All @@ -108,6 +111,7 @@ where

#[cfg(io_lifetimes_use_std)]
#[cfg(windows)]
#[allow(deprecated)]
impl<T: From<OwnedHandle>> FromHandle for T {
#[inline]
fn from_handle(owned_handle: OwnedHandle) -> Self {
Expand All @@ -116,6 +120,7 @@ impl<T: From<OwnedHandle>> FromHandle for T {
}
#[cfg(io_lifetimes_use_std)]
#[cfg(windows)]
#[allow(deprecated)]
impl<T> IntoHandle for T
where
OwnedHandle: From<T>,
Expand All @@ -128,6 +133,7 @@ where

#[cfg(io_lifetimes_use_std)]
#[cfg(windows)]
#[allow(deprecated)]
impl<T: From<OwnedSocket>> FromSocket for T {
#[inline]
fn from_socket(owned_socket: OwnedSocket) -> Self {
Expand All @@ -136,6 +142,7 @@ impl<T: From<OwnedSocket>> FromSocket for T {
}
#[cfg(io_lifetimes_use_std)]
#[cfg(windows)]
#[allow(deprecated)]
impl<T> IntoSocket for T
where
OwnedSocket: From<T>,
Expand Down
Loading