Skip to content

Commit 3857a25

Browse files
committed
Merge pull request #899 from alexcrichton/io-stdio
Amend RFC 517: Add material for stdio
2 parents dab3e4b + 48fbc14 commit 3857a25

File tree

1 file changed

+173
-3
lines changed

1 file changed

+173
-3
lines changed

text/0517-io-os-reform.md

Lines changed: 173 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ follow-up PRs against this RFC.
6262
* [Errors]
6363
* [Channel adapters]
6464
* [stdin, stdout, stderr]
65+
* [Printing functions]
6566
* [std::env]
6667
* [std::fs]
6768
* [Free functions]
@@ -72,7 +73,6 @@ follow-up PRs against this RFC.
7273
* [TCP]
7374
* [UDP]
7475
* [Addresses]
75-
* [std::net] (stub)
7676
* [std::process]
7777
* [Command]
7878
* [Child]
@@ -1155,7 +1155,176 @@ RFC recommends they remain unstable.
11551155
#### `stdin`, `stdout`, `stderr`
11561156
[stdin, stdout, stderr]: #stdin-stdout-stderr
11571157

1158-
> To be added in a follow-up PR.
1158+
The current `stdio` module will be removed in favor of these constructors in the
1159+
`io` module:
1160+
1161+
```rust
1162+
pub fn stdin() -> Stdin;
1163+
pub fn stdout() -> Stdout;
1164+
pub fn stderr() -> Stderr;
1165+
```
1166+
1167+
* `stdin` - returns a handle to a **globally shared** standard input of
1168+
the process which is buffered as well. Due to the globally shared nature of
1169+
this handle, all operations on `Stdin` directly will acquire a lock internally
1170+
to ensure access to the shared buffer is synchronized. This implementation
1171+
detail is also exposed through a `lock` method where the handle can be
1172+
explicitly locked for a period of time so relocking is not necessary.
1173+
1174+
The `Read` trait will be implemented directly on the returned `Stdin` handle
1175+
but the `BufRead` trait will not be (due to synchronization concerns). The
1176+
locked version of `Stdin` (`StdinLock`) will provide an implementation of
1177+
`BufRead`.
1178+
1179+
The design will largely be the same as is today with the `old_io` module.
1180+
1181+
```rust
1182+
impl Stdin {
1183+
fn lock(&self) -> StdinLock;
1184+
fn read_line(&mut self, into: &mut String) -> io::Result<()>;
1185+
fn read_until(&mut self, byte: u8, into: &mut Vec<u8>) -> io::Result<()>;
1186+
}
1187+
impl Read for Stdin { ... }
1188+
impl Read for StdinLock { ... }
1189+
impl BufRead for StdinLock { ... }
1190+
```
1191+
1192+
* `stderr` - returns a **non buffered** handle to the standard error output
1193+
stream for the process. Each call to `write` will roughly translate to a
1194+
system call to output data when written to `stderr`. This handle is locked
1195+
like `stdin` to ensure, for example, that calls to `write_all` are atomic with
1196+
respect to one another. There will also be an RAII guard to lock the handle
1197+
and use the result as an instance of `Write`.
1198+
1199+
```rust
1200+
impl Stderr {
1201+
fn lock(&self) -> StderrLock;
1202+
}
1203+
impl Write for Stderr { ... }
1204+
impl Write for StderrLock { ... }
1205+
```
1206+
1207+
* `stdout` - returns a **globally buffered** handle to the standard output of
1208+
the current process. The amount of buffering can be decided at runtime to
1209+
allow for different situations such as being attached to a TTY or being
1210+
redirected to an output file. The `Write` trait will be implemented for this
1211+
handle, and like `stderr` it will be possible to lock it and then use the
1212+
result as an instance of `Write` as well.
1213+
1214+
```rust
1215+
impl Stdout {
1216+
fn lock(&self) -> StdoutLock;
1217+
}
1218+
impl Write for Stdout { ... }
1219+
impl Write for StdoutLock { ... }
1220+
```
1221+
1222+
#### Windows and stdio
1223+
[Windows stdio]: #windows-and-stdio
1224+
1225+
On Windows, standard input and output handles can work with either arbitrary
1226+
`[u8]` or `[u16]` depending on the state at runtime. For example a program
1227+
attached to the console will work with arbitrary `[u16]`, but a program attached
1228+
to a pipe would work with arbitrary `[u8]`.
1229+
1230+
To handle this difference, the following behavior will be enforced for the
1231+
standard primitives listed above:
1232+
1233+
* If attached to a pipe then no attempts at encoding or decoding will be done,
1234+
the data will be ferried through as `[u8]`.
1235+
1236+
* If attached to a console, then `stdin` will attempt to interpret all input as
1237+
UTF-16, re-encoding into UTF-8 and returning the UTF-8 data instead. This
1238+
implies that data will be buffered internally to handle partial reads/writes.
1239+
Invalid UTF-16 will simply be discarded returning an `io::Error` explaining
1240+
why.
1241+
1242+
* If attached to a console, then `stdout` and `stderr` will attempt to interpret
1243+
input as UTF-8, re-encoding to UTF-16. If the input is not valid UTF-8 then an
1244+
error will be returned and no data will be written.
1245+
1246+
#### Raw stdio
1247+
[Raw stdio]: #raw-stdio
1248+
1249+
> **Note**: This section is intended to be a sketch of possible raw stdio
1250+
> support, but it is not planned to implement or stabilize this
1251+
> implementation at this time.
1252+
1253+
The above standard input/output handles all involve some form of locking or
1254+
buffering (or both). This cost is not always wanted, and hence raw variants will
1255+
be provided. Due to platform differences across unix/windows, the following
1256+
structure will be supported:
1257+
1258+
```rust
1259+
mod os {
1260+
mod unix {
1261+
mod stdio {
1262+
struct Stdio { .. }
1263+
1264+
impl Stdio {
1265+
fn stdout() -> Stdio;
1266+
fn stderr() -> Stdio;
1267+
fn stdin() -> Stdio;
1268+
}
1269+
1270+
impl Read for Stdio { ... }
1271+
impl Write for Stdio { ... }
1272+
}
1273+
}
1274+
1275+
mod windows {
1276+
mod stdio {
1277+
struct Stdio { ... }
1278+
struct StdioConsole { ... }
1279+
1280+
impl Stdio {
1281+
fn stdout() -> io::Result<Stdio>;
1282+
fn stderr() -> io::Result<Stdio>;
1283+
fn stdin() -> io::Result<Stdio>;
1284+
}
1285+
// same constructors StdioConsole
1286+
1287+
impl Read for Stdio { ... }
1288+
impl Write for Stdio { ... }
1289+
1290+
impl StdioConsole {
1291+
// returns slice of what was read
1292+
fn read<'a>(&self, buf: &'a mut OsString) -> io::Result<&'a OsStr>;
1293+
// returns remaining part of `buf` to be written
1294+
fn write<'a>(&self, buf: &'a OsStr) -> io::Result<&'a OsStr>;
1295+
}
1296+
}
1297+
}
1298+
}
1299+
```
1300+
1301+
There are some key differences from today's API:
1302+
1303+
* On unix, the API has not changed much except that the handles have been
1304+
consolidated into one type which implements both `Read` and `Write` (although
1305+
writing to stdin is likely to generate an error).
1306+
* On windows, there are two sets of handles representing the difference between
1307+
"console mode" and not (e.g. a pipe). When not a console the normal I/O traits
1308+
are implemented (delegating to `ReadFile` and `WriteFile`. The console mode
1309+
operations work with `OsStr`, however, to show how they work with UCS-2 under
1310+
the hood.
1311+
1312+
#### Printing functions
1313+
[Printing functions]: #printing-functions
1314+
1315+
The current `print`, `println`, `print_args`, and `println_args` functions will
1316+
all be "removed from the public interface" by [prefixing them with `__` and
1317+
marking `#[doc(hidden)]`][gh22607]. These are all implementation details of the
1318+
`print!` and `println!` macros and don't need to be exposed in the public
1319+
interface.
1320+
1321+
[gh22607]: https://github.com/rust-lang/rust/issues/22607
1322+
1323+
The `set_stdout` and `set_stderr` functions will be removed with no replacement
1324+
for now. It's unclear whether these functions should indeed control a thread
1325+
local handle instead of a global handle as whether they're justified in the
1326+
first place. It is a backwards-compatible extension to allow this sort of output
1327+
to be redirected and can be considered if the need arises.
11591328

11601329
### `std::env`
11611330
[std::env]: #stdenv
@@ -1173,7 +1342,8 @@ and the signatures will be updated to follow this RFC's
11731342

11741343
* `vars` (renamed from `env`): yields a vector of `(OsString, OsString)` pairs.
11751344
* `var` (renamed from `getenv`): take a value bounded by `AsOsStr`,
1176-
allowing Rust strings and slices to be ergonomically passed in. Yields an `Option<OsString>`.
1345+
allowing Rust strings and slices to be ergonomically passed in. Yields an
1346+
`Option<OsString>`.
11771347
* `var_string`: take a value bounded by `AsOsStr`, returning `Result<String,
11781348
VarError>` where `VarError` represents a non-unicode `OsString` or a "not
11791349
present" value.

0 commit comments

Comments
 (0)