Skip to content

Tracking Issue for Stdin::lines forwarder method #87096

Closed
@tlyu

Description

@tlyu

Feature gate: #![feature(stdin_forwarders)]

This is a tracking issue for adding new methods Stdin::lines and Stdin::split that will forward to the corresponding methods on the BufRead implementation of StdinLock. This proposal is related to #86845, and further reduces the obstacles for beginners to write simple interactive terminal programs.

Especially for beginners, reading a sequence of lines from the standard input stream can involve intimidating problems with locking and lifetimes. First, the user has to call the free function stdin() to get a handle on the stream; then, the user would have to call the lock() method to gain access to the lines() method. At this point, lifetime issues rapidly arise: the following code (playground)

use std::io::{self, prelude::*};
fn main() {
    let mut lines = io::stdin().lock().lines();
    loop {
        print!("prompt: ");
        io::stdout().flush();
        if let Some(_line) = lines.next() {
            // do stuff
        } else {
            break;
        }
    }
}

produces this error:

error[E0716]: temporary value dropped while borrowed
 --> src/main.rs:3:21
  |
3 |     let mut lines = io::stdin().lock().lines();
  |                     ^^^^^^^^^^^               - temporary value is freed at the end of this statement
  |                     |
  |                     creates a temporary which is freed while still in use
...
7 |         if let Some(_line) = lines.next() {
  |                              ----- borrow later used here
  |
  = 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 (and the iterator produced by lines()) 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.

Although #86845 makes it easier to obtain locked stdio handles, it would be even better if beginners didn't have to deal with the concept of locking at all at early stages of their learning.

There is precedent in the Stdin::read_line forwarder method that implicitly locks Stdin and calls the BufRead::read_line method. However, read_line() is somewhat difficult to use, because it requires that the user first allocate a String, and it doesn't remove newlines. In contrast, lines() provides an iterator over input lines that removes line endings, including both carriage return (CR) and line feed (LF) characters.

This proposal also includes a split() forwarder method, because it is similar in nature and usability to the lines() method. The remaining exclusive methods of BufRead are less useful to beginners, and require more experience to use.

Public API

// std::io

impl Stdin {
    pub fn lines(self) { /* ... */ }
}

Steps / History

Unresolved Questions

  • During stabilization, we might want to update existing documentation to recommend these forwarder methods, and include examples of their use.

@rustbot label +A-io +D-newcomer-roadblock

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-ioArea: `std::io`, `std::fs`, `std::net` and `std::path`C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFCD-newcomer-roadblockDiagnostics: Confusing error or lint; hard to understand for new users.T-libs-apiRelevant to the library API team, which will review and decide on the PR/issue.disposition-mergeThis issue / PR is in PFCP or FCP with a disposition to merge it.finished-final-comment-periodThe final comment period is finished for this PR / Issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions