The `unstable` feature is not required due to dependency update. Default features are disabled to minimize dependency graph, which is particularly relevant for `wasm32-wasi` target, since default `async-std` feature set adds `socket2` dependency, which does not support `wasm32-wasi` This change should also massively speed up compilation Before: ``` io-lifetimes v1.0.0-rc0 (/home/rvolosatovs/src/github.com/sunfishcode/io-lifetimes) ├── async-std v1.12.0 │ ├── async-channel v1.7.1 │ │ ├── concurrent-queue v1.2.4 │ │ │ └── cache-padded v1.2.0 │ │ ├── event-listener v2.5.3 │ │ └── futures-core v0.3.23 │ ├── async-global-executor v2.2.0 │ │ ├── async-channel v1.7.1 (*) │ │ ├── async-executor v1.4.1 │ │ │ ├── async-task v4.3.0 │ │ │ ├── concurrent-queue v1.2.4 (*) │ │ │ ├── fastrand v1.8.0 │ │ │ │ └── instant v0.1.12 │ │ │ │ └── cfg-if v1.0.0 │ │ │ ├── futures-lite v1.12.0 │ │ │ │ ├── fastrand v1.8.0 (*) │ │ │ │ ├── futures-core v0.3.23 │ │ │ │ ├── futures-io v0.3.23 │ │ │ │ ├── memchr v2.5.0 │ │ │ │ ├── parking v2.0.0 │ │ │ │ ├── pin-project-lite v0.2.9 │ │ │ │ └── waker-fn v1.1.0 │ │ │ ├── once_cell v1.13.1 │ │ │ └── slab v0.4.7 │ │ │ [build-dependencies] │ │ │ └── autocfg v1.1.0 │ │ ├── async-io v1.8.0 │ │ │ ├── concurrent-queue v1.2.4 (*) │ │ │ ├── futures-lite v1.12.0 (*) │ │ │ ├── log v0.4.17 │ │ │ │ ├── cfg-if v1.0.0 │ │ │ │ └── value-bag v1.0.0-alpha.9 │ │ │ │ └── ctor v0.1.23 (proc-macro) │ │ │ │ ├── quote v1.0.21 │ │ │ │ │ └── proc-macro2 v1.0.43 │ │ │ │ │ └── unicode-ident v1.0.3 │ │ │ │ └── syn v1.0.99 │ │ │ │ ├── proc-macro2 v1.0.43 (*) │ │ │ │ ├── quote v1.0.21 (*) │ │ │ │ └── unicode-ident v1.0.3 │ │ │ │ [build-dependencies] │ │ │ │ └── version_check v0.9.4 │ │ │ ├── once_cell v1.13.1 │ │ │ ├── parking v2.0.0 │ │ │ ├── polling v2.3.0 │ │ │ │ ├── cfg-if v1.0.0 │ │ │ │ └── log v0.4.17 (*) │ │ │ │ [build-dependencies] │ │ │ │ └── autocfg v1.1.0 │ │ │ ├── slab v0.4.7 (*) │ │ │ ├── socket2 v0.4.4 │ │ │ └── waker-fn v1.1.0 │ │ │ [build-dependencies] │ │ │ └── autocfg v1.1.0 │ │ ├── async-lock v2.5.0 │ │ │ └── event-listener v2.5.3 │ │ ├── blocking v1.2.0 │ │ │ ├── async-channel v1.7.1 (*) │ │ │ ├── async-task v4.3.0 │ │ │ ├── atomic-waker v1.0.0 │ │ │ ├── fastrand v1.8.0 (*) │ │ │ ├── futures-lite v1.12.0 (*) │ │ │ └── once_cell v1.13.1 │ │ ├── futures-lite v1.12.0 (*) │ │ ├── num_cpus v1.13.1 │ │ │ └── libc v0.2.132 │ │ └── once_cell v1.13.1 │ ├── async-io v1.8.0 (*) │ ├── async-lock v2.5.0 (*) │ ├── async-process v1.5.0 │ │ ├── cfg-if v1.0.0 │ │ ├── event-listener v2.5.3 │ │ ├── futures-lite v1.12.0 (*) │ │ └── once_cell v1.13.1 │ │ [build-dependencies] │ │ └── autocfg v1.1.0 │ ├── crossbeam-utils v0.8.11 │ │ ├── cfg-if v1.0.0 │ │ └── once_cell v1.13.1 │ ├── futures-channel v0.3.23 │ │ └── futures-core v0.3.23 │ ├── futures-core v0.3.23 │ ├── futures-io v0.3.23 │ ├── futures-lite v1.12.0 (*) │ ├── gloo-timers v0.2.4 │ │ ├── futures-channel v0.3.23 (*) │ │ ├── futures-core v0.3.23 │ │ ├── js-sys v0.3.59 │ │ │ └── wasm-bindgen v0.2.82 │ │ │ ├── cfg-if v1.0.0 │ │ │ └── wasm-bindgen-macro v0.2.82 (proc-macro) │ │ │ ├── quote v1.0.21 (*) │ │ │ └── wasm-bindgen-macro-support v0.2.82 │ │ │ ├── proc-macro2 v1.0.43 (*) │ │ │ ├── quote v1.0.21 (*) │ │ │ ├── syn v1.0.99 (*) │ │ │ ├── wasm-bindgen-backend v0.2.82 │ │ │ │ ├── bumpalo v3.11.0 │ │ │ │ ├── log v0.4.17 │ │ │ │ │ ├── cfg-if v1.0.0 │ │ │ │ │ └── value-bag v1.0.0-alpha.9 │ │ │ │ │ └── ctor v0.1.23 (proc-macro) (*) │ │ │ │ │ [build-dependencies] │ │ │ │ │ └── version_check v0.9.4 │ │ │ │ ├── once_cell v1.13.1 │ │ │ │ ├── proc-macro2 v1.0.43 (*) │ │ │ │ ├── quote v1.0.21 (*) │ │ │ │ ├── syn v1.0.99 (*) │ │ │ │ └── wasm-bindgen-shared v0.2.82 │ │ │ └── wasm-bindgen-shared v0.2.82 │ │ └── wasm-bindgen v0.2.82 (*) │ ├── kv-log-macro v1.0.7 │ │ └── log v0.4.17 (*) │ ├── log v0.4.17 (*) │ ├── memchr v2.5.0 │ ├── once_cell v1.13.1 │ ├── pin-project-lite v0.2.9 │ ├── pin-utils v0.1.0 │ ├── slab v0.4.7 (*) │ └── wasm-bindgen-futures v0.4.32 │ ├── cfg-if v1.0.0 │ ├── js-sys v0.3.59 (*) │ └── wasm-bindgen v0.2.82 (*) └── libc v0.2.132 ``` After: ``` io-lifetimes v1.0.0-rc0 (/home/rvolosatovs/src/github.com/sunfishcode/io-lifetimes) ├── async-std v1.12.0 └── libc v0.2.132 ``` Signed-off-by: Roman Volosatovs <rvolosatovs@riseup.net>
This library introduces OwnedFd, BorrowedFd, and supporting types and
traits, and corresponding features for Windows, which implement safe owning
and borrowing I/O lifetime patterns.
This is associated with RFC 3128, the I/O Safety RFC, which is now merged.
Work is now underway to move the OwnedFd and BorrowedFd types and AsFd
trait developed here into std.
Some features currently require nightly Rust, as they depend on rustc_attrs
to perform niche optimizations needed for FFI use cases.
For a quick taste, check out the code examples:
- hello, a basic demo of this API, doing low-level I/O manually, using the provided example FFI bindings
- easy-conversions, demonstrating the
from_intoconvenience feature for converting from animpl Into*into animpl From*. - portable-views, demonstrating the convenience feature which allows one
to temporarily "view" a file descriptor as any owning type such as
File - flexible-apis, demonstrating how to write library APIs that accept untyped I/O resources.
- owning-wrapper, demonstrating how to implement a type which wraps an
Owned*type.
The core of the API is very simple, and consists of two main types and three main traits:
pub struct BorrowedFd<'fd> { ... }
pub struct OwnedFd { ... }
pub trait AsFd { ... }
pub trait IntoFd { ... }
pub trait FromFd { ... }
impl AsRawFd for BorrowedFd<'_> { ... }
impl AsRawFd for OwnedFd { ... }
impl IntoRawFd for OwnedFd { ... }
impl FromRawFd for OwnedFd { ... }
impl Drop for OwnedFd { ... }
impl AsFd for BorrowedFd<'_> { ... }
impl AsFd for OwnedFd { ... }
impl IntoFd for OwnedFd { ... }
impl FromFd for OwnedFd { ... }
On Windows, there are Handle and Socket versions of every Fd thing, and
a special HandleOrInvalid type to cope with inconsistent error reporting
in the Windows API.
The magic of transparency
Here's the fun part. BorrowedFd and OwnedFd are repr(transparent) and
hold RawFd values, and Option<BorrowedFd> and Option<OwnedFd> are
FFI-safe (on nightly Rust), so they can all be used in FFI directly:
extern "C" {
pub fn open(pathname: *const c_char, flags: c_int, ...) -> Option<OwnedFd>;
pub fn read(fd: BorrowedFd<'_>, ptr: *mut c_void, size: size_t) -> ssize_t;
pub fn write(fd: BorrowedFd<'_>, ptr: *const c_void, size: size_t) -> ssize_t;
pub fn close(fd: OwnedFd) -> c_int;
}
With bindings like this, users never have to touch RawFd values. Of course,
not all code will do this, but it is a fun feature for code that can. This
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
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.
Prior Art
There are several similar crates: fd, filedesc, filedescriptor, owned-fd, and unsafe-io.
Some of these provide additional features such as the ability to create pipes or sockets, to get and set flags, and to do read and write operations. io-lifetimes omits these features, leaving them to to be provided as separate layers on top.
Most of these crates provide ways to duplicate a file descriptor. io-lifetimes currently treats this as another feature that can be provided by a layer on top, though if there are use cases where this is a common operation, it could be added.
io-lifetimes's distinguishing features are its use of repr(transparent)
to support direct FFI usage, niche optimizations so Option can support direct
FFI usafe as well (on nightly Rust), lifetime-aware As*/Into*/From*
traits which leverage Rust's lifetime system and allow safe and checked
from_* and as_*/into_* functions, and powerful convenience features
enabled by its underlying safety.
io-lifetimes also has full Windows support, as well as Unix/Windows portability abstractions, covering both file-like and socket-like types.
io-lifetimes's OwnedFd type is similar to
fd's
FileDesc. io-lifetimes
doesn't have a close_on_drop parameter, and instead uses OwnedFd and
BorrowedFd to represent dropping and non-dropping handles, respectively, in
a way that is checked at compile time rather than runtime.
io-lifetimes's OwnedFd type is also similar to
filedesc's
FileDesc
io-lifetimes's OwnedFd reserves the value -1, so it doesn't need to test for
-1 in its Drop, and Option<OwnedFd> (on nightly Rust) is the same size
as FileDesc.
io-lifetimes's OwnedFd type is also similar to
owned-fd's
OwnedFd.
io-lifetimes doesn't implement Clone, because duplicating a file descriptor
can fail due to OS process limits, while Clone is an infallible interface.
io-lifetimes's BorrowedFd is similar to
owned-fd's
FdRef, except it
uses a lifetime parameter and PhantomData rather than transmuting a raw file
descriptor value into a reference value.
io-lifetimes's convenience features are similar to those of
unsafe-io, but io-lifetimes is built on
its own As*/Into*/From* traits, rather than extending
AsRaw*/IntoRaw*/FromRaw* with
OwnsRaw, so
they're simpler and safer to use. io-lifetimes doesn't include unsafe-io's
*ReadWrite* or *HandleOrSocket* abstractions, and leaves these as features
to be provided by separate layers on top.