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