Description
Feature gate: #![feature(stdio_locked)]
This is a tracking issue for adding owned locked handles to stdio.
Especially for beginners, using stdio handles can involve intimidating problems with locking and lifetimes. First, the user has to call a free function (stderr()
, stdin()
, stdout()
) to get a handle on the stream; then, the user might have to call the lock()
method (for example, to gain access to the lines()
iterator constructor). At this point, lifetime issues rapidly arise: the following code (playground) will produce a compile error.
use std::io;
fn main() {
let _h = io::stdin().lock();
}
error[E0716]: temporary value dropped while borrowed
--> src/main.rs:3:14
|
3 | let _h = io::stdin().lock();
| ^^^^^^^^^^^ - temporary value is freed at the end of this statement
| |
| creates a temporary which is freed while still in use
4 | }
| - borrow might be used here, when `_h` is dropped and runs the destructor for type `StdinLock<'_>`
|
= note: consider using a `let` binding to create a longer lived value
The need to create a let
binding to the handle seems confusing and frustrating, especially if the program does not need to use the handle again. The explanation is that the lock behaves as if it borrows the original handle from stdin()
, and the temporary value created for the call to the lock()
method is dropped at the end of the statement, invalidating the borrow. That explanation might be beyond the current level of understanding of a beginner who simply wants to write an interactive terminal program.
Even experienced Rust programmers sometimes have trouble with the lifetime management required for using locked stdio handles: (comment), (comment).
Fortunately, the stdio handles don't contain any non-static references, so it's possible to create owned locks such as StdinLock<'static>
. This proposal creates methods and free functions for creating owned locked stdio handles. The methods consume self
, eliminating any lifetime problems. The free functions directly return an owned locked handle, for programs where there is no need to use an unlocked handle. The implementation currently depends on the mutex references in the stdio handles being static, but this does not preclude future changes to the locking internals (for example, using Arc
to wrap the mutex).
Public API
// std::io
pub fn stderr_locked() -> StderrLock<'static> { /* ... */ }
pub fn stdin_locked() -> StdinLock<'static> { /* ... */ }
pub fn stdout_locked() -> StdoutLock<'static> { /* ... */ }
impl Stderr {
pub fn into_locked(self) -> StderrLock<'static> { /* ... */ }
}
impl Stdin {
pub fn into_locked(self) -> StdinLock<'static> { /* ... */ }
}
impl Stdout {
pub fn into_locked(self) -> StdoutLock<'static> { /* ... */ }
}
Steps / History
- Implementation: add owned locked stdio handles #86799
- Add tracking issue to implementation: stdio_locked: add tracking issue #86846
- Final comment period (FCP)
- Stabilization PR
Unresolved Questions
- During stabilization, might we want to change the documentation to encourage people to prefer the free functions that obtain owned locked handles (
stdin_locked()
, etc.) over the ones that obtain unlocked handles (stdin()
, etc.)?
@rustbot label +A-io +D-newcomer-roadblock