• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1<div align="center">
2  <h1><code>io-lifetimes</code></h1>
3
4  <p>
5    <strong>A low-level I/O ownership and borrowing library</strong>
6  </p>
7
8  <p>
9    <a href="https://github.com/sunfishcode/io-lifetimes/actions?query=workflow%3ACI"><img src="https://github.com/sunfishcode/io-lifetimes/workflows/CI/badge.svg" alt="Github Actions CI Status" /></a>
10    <a href="https://crates.io/crates/io-lifetimes"><img src="https://img.shields.io/crates/v/io-lifetimes.svg" alt="crates.io page" /></a>
11    <a href="https://docs.rs/io-lifetimes"><img src="https://docs.rs/io-lifetimes/badge.svg" alt="docs.rs docs" /></a>
12  </p>
13</div>
14
15This library introduces `OwnedFd`, `BorrowedFd`, and supporting types and
16traits, and corresponding features for Windows, which implement safe owning
17and borrowing I/O lifetime patterns.
18
19This is associated with [RFC 3128], the I/O Safety RFC, which is now merged.
20Work is now underway to move the `OwnedFd` and `BorrowedFd` types and `AsFd`
21trait developed here into `std`.
22
23For a quick taste, check out the code examples:
24
25 - [hello], a basic demo of this API, doing low-level I/O manually, using the
26   [provided example FFI bindings]
27 - [easy-conversions], demonstrating the `from_into` convenience feature for
28   converting from an `impl Into*` into an `impl From*`.
29 - [portable-views], demonstrating the convenience feature which allows one
30   to temporarily "view" a file descriptor as any owning type such as `File`
31 - [flexible-apis], demonstrating how to write library APIs that accept
32   untyped I/O resources.
33 - [owning-wrapper], demonstrating how to implement a type which wraps an
34   `Owned*` type.
35
36[hello]: https://github.com/sunfishcode/io-lifetimes/blob/main/examples/hello.rs
37[easy-conversions]: https://github.com/sunfishcode/io-lifetimes/blob/main/examples/easy-conversions.rs
38[portable-views]: https://github.com/sunfishcode/io-lifetimes/blob/main/examples/portable-views.rs
39[flexible-apis]: https://github.com/sunfishcode/io-lifetimes/blob/main/examples/flexible-apis.rs
40[owning-wrapper]: https://github.com/sunfishcode/io-lifetimes/blob/main/examples/owning-wrapper.rs
41[provided example FFI bindings]: https://github.com/sunfishcode/io-lifetimes/blob/main/src/example_ffi.rs
42
43The core of the API is very simple, and consists of two main types and three
44main traits:
45
46```rust
47pub struct BorrowedFd<'fd> { ... }
48pub struct OwnedFd { ... }
49
50pub trait AsFd { ... }
51pub trait IntoFd { ... }
52pub trait FromFd { ... }
53
54impl AsRawFd for BorrowedFd<'_> { ... }
55impl AsRawFd for OwnedFd { ... }
56impl IntoRawFd for OwnedFd { ... }
57impl FromRawFd for OwnedFd { ... }
58
59impl Drop for OwnedFd { ... }
60
61impl AsFd for BorrowedFd<'_> { ... }
62impl AsFd for OwnedFd { ... }
63impl IntoFd for OwnedFd { ... }
64impl FromFd for OwnedFd { ... }
65```
66
67On Windows, there are `Handle` and `Socket` versions of every `Fd` thing, and
68a special `HandleOrInvalid` type to cope with inconsistent error reporting
69in the Windows API.
70
71## The magic of transparency
72
73Here's the fun part. `BorrowedFd` and `OwnedFd` are `repr(transparent)` and
74hold `RawFd` values, and `Option<BorrowedFd>` and `Option<OwnedFd>` are
75FFI-safe (on Rust >= 1.63), so they can all be used in FFI [directly]:
76
77[directly]: https://github.com/sunfishcode/io-lifetimes/blob/main/src/example_ffi.rs
78
79```rust
80extern "C" {
81    pub fn open(pathname: *const c_char, flags: c_int, ...) -> Option<OwnedFd>;
82    pub fn read(fd: BorrowedFd<'_>, ptr: *mut c_void, size: size_t) -> ssize_t;
83    pub fn write(fd: BorrowedFd<'_>, ptr: *const c_void, size: size_t) -> ssize_t;
84    pub fn close(fd: OwnedFd) -> c_int;
85}
86```
87
88With bindings like this, users never have to touch `RawFd` values. Of course,
89not all code will do this, but it is a fun feature for code that can. This
90is what motivates having `BorrowedFd` instead of just using `&OwnedFd`.
91
92Note the use of `Option<OwnedFd>` as the return value of `open`, representing
93the fact that it can either succeed or fail.
94
95## I/O Safety in Rust
96
97I/O Safety feature is stablized in Rust 1.63. With this version or later,
98io-lifetimes will use and re-export the standard-library types and traits. With
99older versions, io-lifetimes defines its own copy of these types and traits.
100
101io-lifetimes also includes several features which are not (yet?) in std,
102including the portability traits `AsFilelike`/`AsSocketlike`/etc., the
103`from_into_*` functions in the `From*` traits, and [views].
104
105[views]: https://docs.rs/io-lifetimes/*/io_lifetimes/views/index.html
106
107## Prior Art
108
109There are several similar crates: [fd](https://crates.io/crates/fd),
110[filedesc](https://crates.io/crates/filedesc),
111[filedescriptor](https://crates.io/crates/filedescriptor),
112[owned-fd](https://crates.io/crates/owned-fd), and
113[unsafe-io](https://crates.io/crates/unsafe-io).
114
115Some of these provide additional features such as the ability to create pipes
116or sockets, to get and set flags, and to do read and write operations.
117io-lifetimes omits these features, leaving them to to be provided as separate
118layers on top.
119
120Most of these crates provide ways to duplicate a file descriptor. io-lifetimes
121currently treats this as another feature that can be provided by a layer on
122top, though if there are use cases where this is a common operation, it could
123be added.
124
125io-lifetimes's distinguishing features are its use of `repr(transparent)`
126to support direct FFI usage, niche optimizations so `Option` can support direct
127FFI usafe as well (on Rust >= 1.63), lifetime-aware `As*`/`Into*`/`From*`
128traits which leverage Rust's lifetime system and allow safe and checked
129`from_*` and `as_*`/`into_*` functions, and powerful convenience features
130enabled by its underlying safety.
131
132io-lifetimes also has full Windows support, as well as Unix/Windows
133portability abstractions, covering both file-like and socket-like types.
134
135io-lifetimes's [`OwnedFd`] type is similar to
136[fd](https://crates.io/crates/fd)'s
137[`FileDesc`](https://docs.rs/fd/0.2.3/fd/struct.FileDesc.html). io-lifetimes
138doesn't have a `close_on_drop` parameter, and instead uses [`OwnedFd`] and
139[`BorrowedFd`] to represent dropping and non-dropping handles, respectively, in
140a way that is checked at compile time rather than runtime.
141
142io-lifetimes's [`OwnedFd`] type is also similar to
143[filedesc](https://crates.io/crates/filedesc)'s
144[`FileDesc`](https://docs.rs/filedesc/0.3.0/filedesc/struct.FileDesc.html)
145io-lifetimes's `OwnedFd` reserves the value -1, so it doesn't need to test for
146`-1` in its `Drop`, and `Option<OwnedFd>` (on Rust >= 1.63) is the same size
147as `FileDesc`.
148
149io-lifetimes's [`OwnedFd`] type is also similar to
150[owned-fd](https://crates.io/crates/owned-fd)'s
151[`OwnedFd`](https://docs.rs/owned-fd/0.1.0/owned_fd/struct.OwnedFd.html).
152io-lifetimes doesn't implement `Clone`, because duplicating a file descriptor
153can fail due to OS process limits, while `Clone` is an infallible interface.
154
155io-lifetimes's [`BorrowedFd`] is similar to
156[owned-fd](https://crates.io/crates/owned-fd)'s
157[`FdRef`](https://docs.rs/owned-fd/0.1.0/owned_fd/struct.FdRef.html), except it
158uses a lifetime parameter and `PhantomData` rather than transmuting a raw file
159descriptor value into a reference value.
160
161io-lifetimes's convenience features are similar to those of
162[unsafe-io](https://crates.io/crates/unsafe-io), but io-lifetimes is built on
163its own `As*`/`Into*`/`From*` traits, rather than extending
164`AsRaw*`/`IntoRaw*`/`FromRaw*` with
165[`OwnsRaw`](https://docs.rs/unsafe-io/0.6.9/unsafe_io/trait.OwnsRaw.html), so
166they're simpler and safer to use. io-lifetimes doesn't include unsafe-io's
167`*ReadWrite*` or `*HandleOrSocket*` abstractions, and leaves these as features
168to be provided by separate layers on top.
169
170## Minimum Supported Rust Version (MSRV)
171
172This crate currently works on the version of [Rust on Debian stable], which is
173currently Rust 1.48. This policy may change in the future, in minor version
174releases, so users using a fixed version of Rust should pin to a specific
175version of this crate.
176
177[`OwnedFd`]: https://doc.rust-lang.org/stable/std/os/unix/io/struct.OwnedFd.html
178[`BorrowedFd`]: https://doc.rust-lang.org/stable/std/os/unix/io/struct.BorrowedFd.html
179[RFC 3128]: https://github.com/rust-lang/rfcs/blob/master/text/3128-io-safety.md
180