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