Skip to content

E0716: confusing reference to borrow of Stdin value when there's no obvious reference within StdinLock #85383

Closed
@tlyu

Description

@tlyu

Given the following code: (playground)

use std::io::{self, BufRead};
fn main() {
    let locked = io::stdin().lock();
    for line in locked.lines() {
        println!("{}", line.unwrap());
    }
}

The current output is:

   Compiling playground v0.0.1 (/playground)
error[E0716]: temporary value dropped while borrowed
 --> src/main.rs:3:18
  |
3 |     let locked = io::stdin().lock();
  |                  ^^^^^^^^^^^       - temporary value is freed at the end of this statement
  |                  |
  |                  creates a temporary which is freed while still in use
4 |     for line in locked.lines() {
  |                 ------ borrow later used here
  |
  = note: consider using a `let` binding to create a longer lived value

error: aborting due to previous error

For more information about this error, try `rustc --explain E0716`.
error: could not compile `playground`

To learn more, run the command again with --verbose.

I think it's typical for a newcomer to want to get a Lines iterator by chaining method calls like io::stdin().lock().lines(). There's no obvious reason why that shouldn't work. The examples in the std::io documentation do typically show io::stdin() being assigned to a variable prior to calling its .lock() method, but don't explain why that's necessary.

I think the "borrow" terminology is misleading, but there might not be a concise way to describe what's actually going on to the user. The "temporary value is freed at the end of this statement" is probably correct. The "creates a temporary which is freed while still in use" is probably more correctly "creates a temporary that is required to outlive (or live exactly as long as) the result of...", followed by a pointer to the .lock() method invocation, with text of "...this method call". Maybe it should also point out the '_ lifetime argument in the StdinLock<'_> return type.

I tried and failed to find a satisfactory explanation of what exactly StdinLock was borrowing from the temporary Stdin value produced by io::stdin(). When I looked through the library source code, the only references in StdinLock were those created by locking a Mutex, and the Mutex in question is static. I'm guessing the existence of a borrow is assumed rather than inferred from analyzing the function body of io::Stdin::lock(). The assumption might be the result of the anonymous lifetime argument on the output type StdinLock<'_> from io::Stdin::lock(), which imposes a requirement that the StdinLock doesn't outlive (or has the same lifetime as?) the Stdin value that is the receiver of lock().

The correct fix might be to change the locked stdin handles to all use 'static lifetimes, but I will probably file that as a separate issue.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-borrow-checkerArea: The borrow checkerA-diagnosticsArea: Messages for errors, warnings, and lintsA-lifetimesArea: Lifetimes / regionsD-confusingDiagnostics: Confusing error or lint that should be reworked.D-newcomer-roadblockDiagnostics: Confusing error or lint; hard to understand for new users.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions