Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tracking Issue for Stdin::lines forwarder method #87096

Closed
3 of 4 tasks
Tracked by #16
tlyu opened this issue Jul 13, 2021 · 19 comments · Fixed by #95185
Closed
3 of 4 tasks
Tracked by #16

Tracking Issue for Stdin::lines forwarder method #87096

tlyu opened this issue Jul 13, 2021 · 19 comments · Fixed by #95185
Labels
A-io Area: std::io, std::fs, std::net and std::path C-tracking-issue Category: A tracking issue for an RFC or an unstable feature. D-newcomer-roadblock Diagnostics: Confusing error or lint; hard to understand for new users. disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. finished-final-comment-period The final comment period is finished for this PR / Issue. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.

Comments

@tlyu
Copy link
Contributor

tlyu commented Jul 13, 2021

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

@tlyu tlyu added C-tracking-issue Category: A tracking issue for an RFC or an unstable feature. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. labels Jul 13, 2021
@rustbot rustbot added A-io Area: std::io, std::fs, std::net and std::path D-newcomer-roadblock Diagnostics: Confusing error or lint; hard to understand for new users. labels Jul 13, 2021
@joshtriplett
Copy link
Member

These methods have been around for a while, and they seem reasonable. I think it'd be appropriate to consider stabilizing them.

@rfcbot merge

@rfcbot
Copy link

rfcbot commented Oct 25, 2021

Team member @joshtriplett has proposed to merge this. The next step is review by the rest of the tagged team members:

Concerns:

Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!

See this document for info about what commands tagged team members can give me.

@rfcbot rfcbot added proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. labels Oct 25, 2021
@BurntSushi
Copy link
Member

If #86845 ended up making stdin.lock() work, would we still want these methods?

@dtolnay
Copy link
Member

dtolnay commented Oct 27, 2021

#15023 is an accepted RFC to just make let lines = io::stdin().lock().lines() work.

I think I would want to check in with the lang team and/or compiler team on an outlook for that before we start adding surface area like this only motivated by working around it.

@rfcbot concern better temporary lifetimes

@dtolnay
Copy link
Member

dtolnay commented Dec 22, 2021

We discussed this API briefly in the libs-api meeting. After another look, I am on board with these methods since the motivation isn't just about working around the way that temporary lifetimes are currently handled — it's beneficial to be able to iterate lines of stdin without needing to consider that locking needs to be involved.

@rfcbot resolve better temporary lifetimes

@rfcbot rfcbot added the final-comment-period In the final comment period and will be merged soon unless new substantive objections are raised. label Dec 22, 2021
@rfcbot
Copy link

rfcbot commented Dec 22, 2021

🔔 This is now entering its final comment period, as per the review above. 🔔

@rfcbot rfcbot removed the proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. label Dec 22, 2021
@matklad
Copy link
Member

matklad commented Dec 31, 2021

Two non-strong feelings:

  • it feels a bit odd to stabilize this before Tracking Issue for owned locked stdio handles #86845. The .lines() construct indirectly exposes StdinLock<'static> type, which you otherwise can’t observe. I don’t see a way this can be actively problematic though.
  • motivation for stabilizing split seems comparatively weaker than motivation for lines. It’s byte-level split, it yields Vec<u8> rather than String and as such seems like a niche API. Between read_line, lines and split, split wound be the only non-text re-exported API. For a beginner, who might think of stdin as a text stream, .split() which doesn’t split on whitespace, a-la Python, might be confusing.

@rfcbot rfcbot added finished-final-comment-period The final comment period is finished for this PR / Issue. and removed final-comment-period In the final comment period and will be merged soon unless new substantive objections are raised. labels Jan 1, 2022
@rfcbot
Copy link

rfcbot commented Jan 1, 2022

The final comment period, with a disposition to merge, as per the review above, is now complete.

As the automated representative of the governance process, I would like to thank the author for their work and everyone else who contributed.

This will be merged soon.

@rfcbot rfcbot added the to-announce Announce this issue on triage meeting label Jan 1, 2022
@BurntSushi
Copy link
Member

@rfcbot concern split-is-too-niche

Yeah I think there is some concern with split here. If we're adding these routines specifically to work around common use cases, then I'm not sure split is one of them in a way that's consistent with the rest of std's APIs (which really wants all text oriented processing to happen inside String/&str). It looks like the FCP is done though, so I'm not sure if registering a concern is possible? I'm just now catching up with stuff after the holidays.

Also, these APIs are very poorly documented at the moment. I kind of feel like APIs should have full documentation on them before we consider stabilization. For example, I think these APIs should mention the motivation for their existence, in that it permits more convenient line iteration when compared to having to deal with locking stdin explicitly.

@rfcbot rfcbot added the proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. label Jan 3, 2022
@apiraino apiraino removed the to-announce Announce this issue on triage meeting label Jan 6, 2022
@tlyu
Copy link
Contributor Author

tlyu commented Jan 9, 2022

@BurntSushi I'm willing to remove the split forwarder. Can the API documentation improvement happen during stabilization? There were some API documentation changes that I didn't want to make before stabilization, because I would want higher-level std::io documentation to preferentially mention the forwarder method. My understanding is that we can't do that until during stabilization; is that right?

@joshtriplett
Copy link
Member

I'm not opposed to dropping split; lines seems like the most valuable item here.

@tlyu
Copy link
Contributor Author

tlyu commented Jan 20, 2022

I'm not opposed to dropping split; lines seems like the most valuable item here.

Thanks; I'll work on a PR to delete the split forwarder.

@tlyu
Copy link
Contributor Author

tlyu commented Jan 20, 2022

I'm not opposed to dropping split; lines seems like the most valuable item here.

Thanks; I'll work on a PR to delete the split forwarder.

#93134 if someone who's already commented here is interested in reviewing it. maybe @joshtriplett ?

@Patrick-Poitras
Copy link
Contributor

For what its worth I've used split for processing input with a non-endline separator, but I agree that the implementation here is probably not the one we want for the sake of consistency. Perhaps the idea would be worth revisiting but behind another feature flag as to not stop the stabilization of the other method.

matthiaskrgr added a commit to matthiaskrgr/rust that referenced this issue Jan 21, 2022
delete `Stdin::split` forwarder

Part of rust-lang#87096. Delete the `Stdin::split` forwarder because it's seen as too niche to expose at this level.

`@rustbot` label T-libs-api A-io
@joshtriplett
Copy link
Member

@BurntSushi The removal of split is about to be merged; that should address your concern.

@BurntSushi
Copy link
Member

@rfcbot resolve split-is-too-niche

@rfcbot rfcbot added final-comment-period In the final comment period and will be merged soon unless new substantive objections are raised. and removed proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. labels Jan 22, 2022
@rfcbot
Copy link

rfcbot commented Jan 22, 2022

🔔 This is now entering its final comment period, as per the review above. 🔔

@m-ou-se m-ou-se changed the title Tracking Issue for Stdin::lines, Stdin::split forwarder methods Tracking Issue for Stdin::lines, ~~Stdin::split~~ forwarder method~~s~~ Jan 22, 2022
@m-ou-se m-ou-se changed the title Tracking Issue for Stdin::lines, ~~Stdin::split~~ forwarder method~~s~~ Tracking Issue for Stdin::lines forwarder method Jan 22, 2022
@m-ou-se
Copy link
Member

m-ou-se commented Feb 3, 2022

Looks like rfcbot is sleeping (:


🤖 📣

The final comment period, with a disposition to merge, as per the review above, is now complete.

As the temporary human substitute for the temporarily unavailable automated representative of the governance process, I would like to thank the author for their work and everyone else who contributed.

This will be merged soon.

@m-ou-se m-ou-se removed the final-comment-period In the final comment period and will be merged soon unless new substantive objections are raised. label Feb 3, 2022
@scottmcm
Copy link
Member

scottmcm commented Feb 3, 2022

rfcbot commented earlier (#87096 (comment)), so I guess it got confused by the concern+resolve after the FCP had "completed".

Dylan-DPC added a commit to Dylan-DPC/rust that referenced this issue Apr 6, 2022
@bors bors closed this as completed in d2f1a0b Apr 7, 2022
workingjubilee pushed a commit to tcdi/postgrestd that referenced this issue Sep 15, 2022
AurumTheEnd added a commit to grolig/pv281 that referenced this issue Sep 23, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-io Area: std::io, std::fs, std::net and std::path C-tracking-issue Category: A tracking issue for an RFC or an unstable feature. D-newcomer-roadblock Diagnostics: Confusing error or lint; hard to understand for new users. disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. finished-final-comment-period The final comment period is finished for this PR / Issue. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.