1//! io-lifetimes provides two different options for library authors
2//! writing APIs which accept untyped I/O resources.
3//!
4//! The following uses the POSIX-ish `Fd` types; similar considerations
5//! apply to the Windows and portable types.
6
7#[cfg(all(feature = "close", not(windows)))]
8use io_lifetimes::{AsFd, BorrowedFd, OwnedFd};
9
10/// The simplest way to accept a borrowed I/O resource is to simply use a
11/// `BorrwedFd` as an argument. This doesn't require the function to have any
12/// type parameters. It also works in FFI signatures, as `BorrowedFd` and (on
13/// Rust >= 1.63) `Option<BorrowedFd>` are guaranteed to have the same layout
14/// as `RawFd`.
15///
16/// Callers with an `AsFd`-implementing type would call `.as_fd()` and pass
17/// the result.
18#[cfg(all(feature = "close", not(windows)))]
19fn use_fd_a(fd: BorrowedFd<'_>) {
20    let _ = fd;
21}
22
23/// Another way to do this is to use an `AsFd` type parameter. This is more
24/// verbose at the function definition site, and entails monomorphization, but
25/// it has the advantage of allowing users to pass in any type implementing
26/// `AsFd` directly, without having to call `.as_fd()` themselves.
27#[cfg(all(feature = "close", not(windows)))]
28fn use_fd_b<Fd: AsFd>(fd: Fd) {
29    let _ = fd.as_fd();
30}
31
32/// Another way to do this is to use an `impl AsFd` parameter.
33#[cfg(all(feature = "close", not(windows)))]
34fn use_fd_c(fd: impl AsFd) {
35    let _ = fd.as_fd();
36}
37
38/// The simplest way to accept a consumed I/O resource is to simply use an
39/// `OwnedFd` as an argument. Similar to `use_fd_a`, this doesn't require the
40/// function to have any type parameters, and also works in FFI signatures.
41///
42/// Callers with an `IntoFd`-implementing type would call `.into_fd()` and pass
43/// the result.
44#[cfg(all(feature = "close", not(windows)))]
45fn consume_fd_a(fd: OwnedFd) {
46    let _ = fd;
47}
48
49/// Another way to do this is to use an `IntoFd` type parameter. Similar to
50/// `use_fd_b`, this is more verbose here and entails monomorphization, but it
51/// has the advantage of allowing users to pass in any type implementing
52/// `IntoFd` directly.
53#[cfg(all(feature = "close", not(windows)))]
54fn consume_fd_b<Fd: Into<OwnedFd>>(fd: Fd) {
55    let _: OwnedFd = fd.into();
56}
57
58/// Another way to do this is to use an `impl IntoFd` parameter.
59#[cfg(all(feature = "close", not(windows)))]
60fn consume_fd_c(fd: impl Into<OwnedFd>) {
61    let _: OwnedFd = fd.into();
62}
63
64/// Now let's see how the APIs look for users.
65#[cfg(all(feature = "close", not(windows)))]
66fn main() {
67    let f = std::fs::File::open("Cargo.toml").unwrap();
68
69    // The simple option requires an `.as_fd()` at the callsite.
70    use_fd_a(f.as_fd());
71
72    // Another option can take a reference to any owning type directly.
73    use_fd_b(&f);
74
75    // Of course, users can still pass in `BorrowedFd` values if they want to.
76    use_fd_b(f.as_fd());
77
78    // The other option is `impl AsFd`.
79    use_fd_c(&f);
80
81    // Users can still pass in `BorrowedFd` values if they want to here too.
82    use_fd_c(f.as_fd());
83
84    let a = std::fs::File::open("Cargo.toml").unwrap();
85    let b = std::fs::File::open("Cargo.toml").unwrap();
86    let c = std::fs::File::open("Cargo.toml").unwrap();
87
88    // The simple option requires an `.into()` at the callsite.
89    consume_fd_a(a.into());
90
91    // Another option can take any `Into<OwnedFd>` type directly.
92    consume_fd_b(b);
93
94    // The other option can take any `Into<OwnedFd>` type directly.
95    consume_fd_c(c);
96}
97
98#[cfg(windows)]
99fn main() {
100    println!("This example uses non-Windows APIs.");
101}
102
103#[cfg(all(not(feature = "close"), not(windows)))]
104fn main() {
105    println!("This example requires the \"close\" feature.");
106}
107