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)))]
8 use 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)))]
use_fd_a(fd: BorrowedFd<'_>)19 fn 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)))]
use_fd_b<Fd: AsFd>(fd: Fd)28 fn 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)))]
use_fd_c(fd: impl AsFd)34 fn 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)))]
consume_fd_a(fd: OwnedFd)45 fn 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)))]
consume_fd_b<Fd: Into<OwnedFd>>(fd: Fd)54 fn 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)))]
consume_fd_c(fd: impl Into<OwnedFd>)60 fn 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)))]
main()66 fn 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)]
main()99 fn main() {
100 println!("This example uses non-Windows APIs.");
101 }
102
103 #[cfg(all(not(feature = "close"), not(windows)))]
main()104 fn main() {
105 println!("This example requires the \"close\" feature.");
106 }
107