Skip to content

print! macro should flush stdout #23818

Closed

Description

@tanadeau

As stdout is line-buffered, stdout is not implicitly flushed until a new-line is encountered. This means that the print! macro does not act like a println! without the newline as the documentation suggests. To be equivalent, the user must explicitly flush stdout like the following:

use std::io::prelude::*;                                                           
use std::io;                                                                       

fn main() {
    print!("Type something: ");
    io::stdout().flush().ok().expect("Could not flush stdout");

    // Read stdin, etc.
}

For easy use of the print macros, the user should not need to know about how I/O flushing works. As such, print! should explicitly flush stdout itself.

Activity

  1. sfackler commented on Mar 29, 2015

    @sfackler
    Member

    As a quick survey, standard output is line buffered in C, C++, Python, and Ruby. Go's Printf buffers output and writes it to an unbuffered stdout. Java doesn't document what it does, but System.out appears to autoflush.

  2. richo commented on Mar 29, 2015

    @richo
    Contributor

    I opened #23823. It's not entirely clear to me that the solution isn't to just update the docs to point out that stdout in unbuffered, but I think at the point where you're invoking the print*! machinery you're sufficiently far from the OS that you probably just do want some output to appear in the terminal.

  3. tanadeau commented on Mar 29, 2015

    @tanadeau
    ContributorAuthor

    @richo The implementation of std::io::Stdout is definitely line-buffered. See

    inner: Arc<Mutex<LineWriter<StdoutRaw>>>,

  4. richo commented on Mar 29, 2015

    @richo
    Contributor

    Sure, but that's orthogonal, since the underlying fd is not, and writing to it will not emit anything until it's flushed.

    I'm pretty sure that the correct approach is to merge that patch, but figured it was worth pointing out that updating the docs may be a valid approach too.

  5. alexcrichton commented on Mar 29, 2015

    @alexcrichton
    Member

    It's not 100% clear to me that this is the behavior that we want. In #23087 it was explicitly removed due to worries about deadlocking. The specific problem of deadlocking can be papered over, but in general APIs tend to work much better over time if they do precisely what you expect, and it may not always be expected that print! flushes the output buffer.

    One downside of flushing on print! would be that any form of "maximal buffering" when redirected to a TTY would be lost. For example C performs much more buffering when stdout is redirected than when it is to a console. This means that simple benchmarks which print information will be much slower in Rust than in comparison with others (due to the frequent flushing).

    I would be fine beefing up the documentation in this regard, of course!

  6. richo commented on Mar 29, 2015

    @richo
    Contributor

    That's totally reasonable, and mostly what I expected. Do you have a
    citation about the c buffering though? That wasn't be belief and I'd love
    to read more.

    Will update the docs if no one beats me to it.

    On Saturday, March 28, 2015, Alex Crichton notifications@github.com wrote:

    It's not 100% clear to me that this is the behavior that we want. In
    #23087 #23087 it was explicitly
    removed due to worries about deadlocking. The specific problem of
    deadlocking can be papered over, but in general APIs tend to work much
    better over time if they do precisely what you expect, and it may not
    always be expected that print! flushes the output buffer.

    One downside of flushing on print! would be that any form of "maximal
    buffering" when redirected to a TTY would be lost. For example C performs
    much more buffering when stdout is redirected than when it is to a console.
    This means that simple benchmarks which print information will be much
    slower in Rust than in comparison with others (due to the frequent
    flushing).

    I would be fine beefing up the documentation in this regard, of course!

    —
    Reply to this email directly or view it on GitHub
    #23818 (comment).

  7. tanadeau commented on Mar 29, 2015

    @tanadeau
    ContributorAuthor

    While making the documentation clearer would certainly be helpful, I think Rust is losing ease of use by making auto-flushing more difficult than need be. C++ has std::unitbuf to making auto-flushing simple in cases like prompts and progress bars. This at least makes it so that users don't need to call flush() explicitly after every call.

  8. tanadeau commented on Mar 29, 2015

    @tanadeau
    ContributorAuthor

    Could there be new macros for this use case? I'd hate for Rust I/O to be harder to use correctly than C++'s especially in a common learning case. For example, since C++ does flush cout when cin is read, the following works fine there but the equivalent doesn't for Rust:

    #include <iostream>
    
    int main() {
        std::cout << "Type something: ";
    
        std::string input;
        std::cin >> input;
    
        std::cout << input << std::endl;
    } 

    Also according to the C standard:

     At program startup, three text streams are predefined and need not be opened explicitly
    — standard input (for reading conventional input), standard output (for writing
    conventional output), and standard error (for writing diagnostic output). As initially
    opened, the standard error stream is not fully buffered; the standard input and standard
    output streams are fully buffered if and only if the stream can be determined not to refer
    to an interactive device.
    
  9. nagisa commented on Mar 29, 2015

    @nagisa
    Member

    This means that the print! macro does not act like a println! without the newline as the documentation suggests.

    println! macro, on the other hand, does not specify anything about its flushing semantics either. Even the std::io::Stdout doesn’t and totally should.


    I generally really dislike implicit flushing and prefer to flush explicitly. On the other hand, there’s write{,ln}!, which does not and can’t assume anything about buffering, so I’m ambivalent about this issue.

  10. alexcrichton commented on Mar 30, 2015

    @alexcrichton
    Member

    I think Rust is losing ease of use by making auto-flushing more difficult than need be.

    The design here is generally mostly about trade-offs. On one hand you have "ease of use" where you don't have to worry about calling flush(), but on the other hand you have worries about deadlocking, and/or surprising behavior happening behind the scenes. On the other hand you have to write a few more flush calls, but you know precisely what is happening and can avoid situations like deadlocking fairly easily.

    For example, what should this code do?

    let input = io::stdin();
    let mut locked = input.lock();
    
    print!("enter input: ");
    let mut input = String::new();
    try!(locked.read_line(&mut input));
    
    println!("you entered: {}", input);

    If print! were to auto-flush, then it would deadlock the current process because the stdin handle was already locked. The alternative here is adding one call to try!(locked.flush()) right above the call to read_line.

  11. nagisa commented on Mar 30, 2015

    @nagisa
    Member

    If print! were to auto-flush, then it would deadlock the current process because the stdin handle was already locked. The alternative here is adding one call to try!(locked.flush()) right above the call to read_line.

    I think you misunderstood what the issue is about: it is only proposed to flush std_out_ after print! writes to the std_out_. i.e. change

    macro_rules! print(…) {
         …
    }

    to

    macro_rules! print(…) {
        …
         stdout().flush()
    }

    Since print! already takes a lock to write, this change will not introduce any more new cases where it deadlocks. For example snippet below deadlocks currently and will deadlock in all the same cases if we made it to auto-flush.

    let output = io::stdout();
    let output = output.lock();
    print!("stdout locked"); // deadlock.

    I’m starting to believe that stabilising Std{in,out,err}::lock was a wrong and hasty move.

  12. alexcrichton commented on Mar 30, 2015

    @alexcrichton
    Member

    I think you misunderstood what the issue is about: it is only proposed to flush stdout after print! writes to the stdout

    Hm yes, I think I have misunderstood! There are definitely performance considerations which affect flush-on-all-print semantics as it's not something that other languages tend to do and can greatly hinder various benchmarks.

  13. richo commented on Mar 30, 2015

    @richo
    Contributor

    I think the crux of this is basically "What is the intent of print!". My
    intuition is that it's not intended to be in the critical section, and is
    instead meant to be a helper for "make this string appear on stdout", I
    could really take or leave whether or not it flushes. That said,
    considering that it already traverses all of the formatting machinery
    anyway, if it can't deadlock my intuition is that the flush is just one
    extra syscall to make this do something more user friendly/intuitive.

    While I don't believe it's actually on the table, I would be a stoic -1 to
    any suggestion of making all stdout writes autoflush.

    On Mon, Mar 30, 2015 at 1:50 PM, Alex Crichton notifications@github.com
    wrote:

    I think you misunderstood what the issue is about: it is only proposed to
    flush stdout after print! writes to the stdout

    Hm yes, I think I have misunderstood! There are definitely performance
    considerations which affect flush-on-all-print semantics as it's not
    something that other languages tend to do and can greatly hinder various
    benchmarks.

    —
    Reply to this email directly or view it on GitHub
    #23818 (comment).

  14. tanadeau commented on Mar 30, 2015

    @tanadeau
    ContributorAuthor

    To address the performance considerations, could the print! macro only flush stdout if stdout is attached to a TTY?

  15. 65 remaining items

  16. Lucretiel commented on Dec 18, 2018

    @Lucretiel
    Contributor

    I'd be okay with a macro that guarantees a flush, but I'd be concerned about naming it printf, since that's such a ubiquitous identifier in many other languages for "formatted print"

  17. anyputer commented on Jan 31, 2019

    @anyputer

    How about flprint!, flprintln! macros? Doing printflln! or printlnfl! would look confusing. Is it a problem if it starts with fl cause that might makes it sound like the flushing happens first?

  18. dancojocaru2000 commented on Mar 19, 2020

    @dancojocaru2000

    The main problem about print! not flushing is when combining it with read! from the text_io create.

    Considering this snippet of code:

    use text_io::read;
     
    let rect = Rectangle {
        length: {
            print!("L: ");
            read!()
        },
        width: {
            print!("l: ");
            read!()
        }
    };

    The expected behaviour is this:

    L: 5
    l: 3
    
    

    What actually happens is this:

    5
    3
    L: l: 
    

    C++ buffers std::cout, however it ties it to std::cin so that std::cout is flushed before std::cin reads any data, which solves this problem. This is not possible in this case for obvious reasons, so an alternative (perhaps a flprint! macro as suggested above?) should be found.

  19. frol commented on Mar 19, 2020

    @frol
    Contributor

    @dancojocaru2000

    This is not possible in this case for obvious reasons

    Sorry, what are those reasons? It seems that in this particular case a special flush+read function/macro is a good bet, so it can be even part of the text_io crate.

  20. dancojocaru2000 commented on Mar 19, 2020

    @dancojocaru2000

    This is not possible in this case for obvious reasons

    Sorry, what are those reasons? It seems that in this particular case a special flush+read function/macro is a good bet, so it can be even part of the text_io crate.

    print! is just a macro for dumping stuff into stdout, and it has no connection to anything else (I would assume). A flush+read fn/macro would unnecessarily do two things in one package, restricting flushing only to the case where a read is required.

    Having the option to print and flush in one macro, unbounded from needing to also read, seems like a good addition to the language.

  21. samclaus commented on Apr 28, 2020

    @samclaus

    My guess is that a large percentage of folks will (or should) read the second edition book to get started. I would recommend changing the chapter two exercise code to use print and io::stdout().flush().unwrap() for the guess prompt. This way newbies (like myself) will just grok the rustafarian way to handle line buffered stdin. I'll try to put forth a pull request on the docs if someone has not already tried it.

    THIS. I am re-reading the book trying to get into Rust again and right at the beginning of the Guessing Game chapter I recalled running into issues with flushing the first time I read the book and used print! instead of println! (so the guess would be on the same line, after a colon). Curiosity lead me here.

    If no changes are going to be made for print!, the book should definitely use it and mention the flushing semantics.

    With that said, I personally think println! and print! are used for simple logging or when getting input from the user 90% of the time. Someone also mentioned that the line-flushing is implemented at the writer level and could change under people's feet who expected println! to always flush (like me). Why not cater to the 90% use case and make these both more user-friendly with guaranteed flushing? For the 10% writing performance-sensitive formatting code, explicitness, and usage of low-level APIs is probably already mandatory.

  22. dancojocaru2000 commented on Apr 28, 2020

    @dancojocaru2000

    Furthermore, most people dislike reading a book (even online) about every detail of the language. I know this is a sensible topic, bringing reactions like "how can you not read the book?" (reactions which, imo, drive people away), but Rust By Example should certainly have a topic discussing this.

    Python is pretty much the only other language where the default way of printing doesn't flush at least when input is needed, yet they have the input function which covers this exact purpose of asking questions and waiting for answers.

  23. dancojocaru2000 commented on Jan 3, 2021

    @dancojocaru2000

    @dtolnay

    People writing interactive command-line apps need to understand what flushing means

    In no other programming language that I worked with is that a need. It is merely an "oh, that's cool".

    Furthermore, Rust breaks an expectation that is fulfilled by other languages: input given without the full output shown is garbage, unwanted input.

    Here are two examples:

    • it doesn't make sense that a line read contains the name if the "Name: " prompt wasn't flushed and therefore displayed beforehand
    • when communicating with a server, assuming the output to the server is buffered, if you make a request but then don't flush the request and then you block waiting the answer, it is safe to assume that the server never received the full request and therefore an answer will never be sent, the block on read being therefore infinite

    Another question would be why print! and println! exist in the first place while a read! or readline! macro don't. Furthermore, why is there a need for print! when write! can be used? If their purpose is to make things easier, then why not go the extra step and fulfil the expectation that, when data is read from the standard input, all the data must be flushed to standard output?

  24. deleted a comment from inliquid on Mar 27, 2021
  25. cdecompilador commented on Jul 12, 2021

    @cdecompilador

    This is my personal suggestion to the problem:

    print!(shouldflush, "{}", args...);

    being shouldflush just an optional token, as far as I know this is possible.

    fn main() {
        let a = 5;
    
        // Normal printing, as is now
        print!("{}", a);
    
        // Some io borrow
    
        // Printing with flushing
        print!(shouldflush, "{}", a);
    }

    Maybe other options might be passing a boolean, or flush, or shouldflush = true

  26. pawkw commented on Jul 12, 2021

    @pawkw

    I am new to Rust and ended up here with this problem. I note that print commands in all languages default to displaying on the screen rather than actually printing anything on the printer. Those days have passed. If I were to name an autoflushing macro, I would call it display!

  27. LollipopFt commented on Feb 24, 2022

    @LollipopFt

    what is the difference between print! and write!? aren't they basically the same?

  28. Lucretiel commented on Feb 24, 2022

    @Lucretiel
    Contributor

    what is the difference between print! and write!? aren't they basically the same?

    They are basically the same; write! allows you to write to any stream, whereas print! specifically writes to stdout.

  29. bazzilic commented on Sep 26, 2023

    @bazzilic

    Just to add my 5 cents, I think print! should not flush because line-buffered output is the expected behavior, but

    1. there should be a version of the print macro that explicitly flushes; and
    2. stdlib functions that read from stdin should first explicitly flush stdout.
  30. rcls commented on Apr 18, 2025

    @rcls

    As a quick survey, standard output is line buffered in C, C++, Python, and Ruby. Go's Printf buffers output and writes it to an unbuffered stdout. Java doesn't document what it does, but System.out appears to autoflush.

    Note that the C and python behaviour (at least on Linux) is a bit more nuanced: It is line buffered when stdout is a terminal, and 1kB block buffered otherwise.

    For processing large text files (either in a pipeline or writing to a file), the "unbuffered when not a tty" can make a huge performance difference (e.g., two orders of magnitude).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-feature-requestCategory: A feature request, i.e: not implemented / a PR.T-langRelevant to the language teamT-libs-api[DEPRECATED; DO NOT USE]

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions