Taking an input and printing stuff in rust
Tags: rust
Published: 2023-10-01 06:43:12

In python taking input from the user is as simple as using the built-in `input` function, assigning it to a variable and using it as we need. Below is a simple program which takes in a name from a user and prints out a greeting

```python
name = input("Please enter your name: ")
print(f"Hello {name}")

# Output:
# Enter your name: John Smith
# Hello John Smith
```

In rust, and in many low level languages, to capture a user input we first have to read from `stdin` and store it in a buffer which can then be used as needed. Let's see how this looks all put together

> A small note on stdin, stdout, stderr
>
> In a nutshell
>
> - `stdin` -> Standard Input, your process reads information from you
> - `stdout` -> Standard Output, your process writes normal output
> - `stderr` -> Standard Output, your process writes debug output
>   for more detailed information read the wikipedia page on this topic [StandardStreams](https://en.wikipedia.org/wiki/Standard_streams)

Let's get back to our rust program

```rust
fn main() {
    // Prints to the standard output, with a newline.
    println!("Please enter your name: ");

    // create an empty buffer before hand
    let mut name_buf = String::new();
    // create a `Stdin` object
    let stdin_handle = std::io::stdin();
    // read a line from stdin and store it in buffer `name_buf`
    stdin_handle.read_line(&mut name_buf).unwrap();

    println!("hello {}", name_buf);
}

// Output:
// Enter your name:
// John Smith
// hello John Smith
```

_NOTE: I am going to use unwrap instead of handling errors to have less things to worry about_

If we look closely there is a small difference in the two programs. In python the prompt and name input both happen on same line but in our rust program the input happens on a new line

```
RUST
Enter your name:
John Smith  <- input on a new line
hello John Smith

PYTHON
Enter your name: John Smith <- input the same line
Hello John Smith
```

What if we want the similar behaviour in rust? We some how need to avoid printing a new line after the prompt. For this we can use `print!` macro for this. Reading the [`print!` docs](https://doc.rust-lang.org/std/macro.print.html) ...

> Prints to the standard output.
>
> Equivalent to the println! macro except that a newline is not printed at the end of the message.

Let's try this out

```rust
fn main() {
    // Prints to the standard output, without a newline.
    print!("Greetings, please enter your name: ");

    let mut name_buf = String::new();
    let stdin_handle = std::io::stdin();
    stdin_handle.read_line(&mut name_buf).unwrap();

    println!("hello {}", name_buf);
}

// Output:
// John Smith
// Enter your name: hello John Smith
```

This output was not what we expected. But before understanding what's going on let's try something that fixes this and work our way backwards. Let's try `std::io::stdout().flush().unwrap()` from one of the [examples in the docs](https://doc.rust-lang.org/std/macro.print.html#examples)

```rust
// flush method comes from `Write` trait. We need this in scope
use std::io::Write;

fn main() {
    print!("Enter your name: ");
    // immediately "push"(flush) buffer to stdout
    std::io::stdout().flush().unwrap();
    let mut buf = String::new();
    std::io::stdin().read_line(&mut buf).unwrap();
    println!("hello {buf}");
}

// Output:
// Enter your name: John Smith
// hello John Smith
```

Yay, we go the output we wanted. The reason for earlier "bad" output was that `stdout` is usually line buffered i.e once there is a new line character in the buffer, the buffer will then immediately be flushed/printed to `stdout`. Taking output from first `print!` program above analyzing

```
-----PROGRAM STARTS-----

John Smith
^--- input prompt in buffer is not flushed until we type name and hit enter(newline) key

Enter your name: hello John Smith
^--- prompt and  ^--- greeting are flushed to stdout together causing "bad" output

-----PROGRAM ENDS-----
```

To avoid such issues we can use `std::io::stdio().flush()` as needed to push data to stdout immediately.

## One more difference between `print!` & `println!`

Another difference between `print!` and `println!` is that the `print!` doesn't accept empty args but you can call `println!` without any args

```rust
// Program 1
fn main() {
    println!();
    println!("Hello World");
}

// Output:
//                <- empty line
// Hello World

// Program 2
fn main() {
    print!(); // <- this will fail to compile
    print!("hello World");
}

// Output:
// error: requires at least a format string argument
```

To find out we can look at source of [`print!`](https://doc.rust-lang.org/src/std/macros.rs.html#79-83) & [`println!`](https://doc.rust-lang.org/src/std/macros.rs.html#132-139) macros

```rust
macro_rules! print {
    ($($arg:tt)*) => {{
        $crate::io::_print($crate::format_args!($($arg)*));
    }};
}

macro_rules! println {
    () => {
        $crate::print!("\n")
    };
    ($($arg:tt)*) => {{
        $crate::io::_print($crate::format_args_nl!($($arg)*));
    }};
}

```

Looking at the source we find that if there are no args passed to `println!` we simply print a new line using, `print!` macro. But such is not the case for `print!` macro, we have to pass in some arguments or it won't compile.

Hopefully you understand a couple of differences between `print!` and `println!`. Now diving a little deeper

## Diving deeper

If `stdout` is line buffered then it is a good guess that there must be a size limit to that buffer. Let's try to find its true and try to find the limit

_Note: I am doing this on a 64 bit windows machine with 1.72 rustc. Findings may be different on different machines. This may not be the right way, I am just having fun tinkering and sharing what I found_

```rust
fn main() {
    let array = [1; 500];
    print!("{array:?}");
    let mut buf = String::new();
    std::io::stdin().read_line(&mut buf).unwrap();
    println!("hello {buf}");
}

// Output:
// [1, 1, 1, 1, 1 ..(repeated around 336 times) JohnSmith
                                            //  ^ input
// ...(remaining) 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]hello JohnSmith
                                            // ^ output
```

When we try to print out an array of 1s of length 500, we see some of them are printed before taking input and some after. We can then make a guess, the first portion of 1s might have been written out as it exceeded the buffer limit and remaining output stays in buffer until flushed later. We can try to test this hypothesis by increasing size of array from 500 -> 700. You can try this for yourself, you should see that the first output portion of array is doubled.

```
// Output:
// [1, 1, 1, 1, 1 ..(repeated around 660 times) JohnSmith
                                            //  ^ input
// ...(remaining) 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]hello JohnSmith
                                            // ^ output
```

### Getting a little side tracked on formatter

There was a small thing that was irking me in the above example. Why only 341 `1's` are printed. It must be that the debug format adding brackets, spaces and commas adding to buffer size. Okay, now on to finding the size of the debug printed array

```rust
#![feature(fmt_internals)]

use std::fmt::{Formatter, Debug};

fn main() {
    let arr_341 =  [1; 341];
    let mut fmt_buf = String::new();
    let mut fmtr = Formatter::new(&mut fmt_buf);
    arr_341.fmt(&mut fmtr).unwrap();
    println!("BUF SIZE: {}", std::mem::size_of_val(&*fmt_buf));
}

// Output:
// BUF SIZE: 1023
```

There is a little more going on here than some of the earlier examples. Going from top to down

- `#![feature(fmt_internals)]` -> this is a flag that allows the use of constructing `Formatter` object as `fmt_internals` is a unstable feature and shouldn't be used in normal code. Also this requires rust nightly
- `fmt_buf` -> is an owned string
- `Formatter::new()` -> constructs a new formatter object with the passed in buffer
- `std::mem::size_of_val()` -> gives us the size of an object through shared reference
- `&*fmt_buf` -> we have to dereference first because `&fmt_buf` is a `&String` fat pointer with size 24 bytes(address, length, capacity) and we want the size of what the pointer is pointing to and not the size of pointer itself. `*fmt_buf` is a `str` object and we can pass a refernce(`&*fmt_buf`) to this object to `size_of_val` function to get the correct size

Now if I increase the length of array from 341 to 342, first the size of the debug format buffer becomes `1026` and intitally `1024` are printed out and then the remaining `2` bytes. Try the below program to verify it yourself

```rust
#![feature(fmt_internals)]

use std::{fmt::{Formatter, Debug}, time::Duration};

fn main() {
    let arr_342 =  [1; 342];
    let mut fmt_buf = String::new();
    let mut fmtr = Formatter::new(&mut fmt_buf);
    arr_342.fmt(&mut fmtr).unwrap();
    print!("{arr_342:?}");

    std::thread::sleep(Duration::from_millis(5000));
    println!();
    println!("BUF {}", std::mem::size_of_val(&*fmt_buf));
}

// Output:
// [1, 1, 1, ...(repeated until 1024 bytes) |sleep 5sec| 1]
//                                                       ^^The last 2 bytes  are printed after the 5sec sleep.
```

### Sucker punch on our expectation

The theory so far is that the usual buffer size while writing to `stdout` is 1024 bytes and anything less will not be flushed immediately. Testing on an array supports this theory. Now on to try on different data structures. Let's try a `String` with the size of 1000 bytes or something less than our guessed size limit for output buffer

```rust
use std::{thread::sleep, time::Duration};

fn main() {
    let aa_string = "a".repeat(1000);
    print!("{aa_string}");


    sleep(Duration::from_millis(3000));

    println!();
    println!("STR SIZE: {}", std::mem::size_of_val(&*aa_string));
}

// Output:
// |Sleep for 3sec|aa... (repeated 1000 times)
// STR SIZE: 1000
```

As expected, the program sleeps first as the buffer is not exceeding the limit. Now let's try it with something that exceeds the 1024 bytes

```rust
// same as prev fuc except this line
    let aa_string = "a".repeat(1224);
//

// Output:
// aa... (repeated 1225 times)|Sleep for 3sec|
// STR SIZE: 1225
```

But this is not following our array example. According to our theory we should get the first 1024 bytes, sleep for 3 seconds, get remaining 200 bytes, then STR SIZE output. This example shows there is something more than just fixed buffer size for outputting stuff to `stdout`.

Unfortunately, this is as far as I got for now. I will dig into this topic more and will write another blog if I find something. In the mean time if any of you people reading this have more information kindly point me to those resources. I will be very grateful.

Whew, that's a long story on `print!` & `println!` . I had fun learning and experimenting about these things and hopefully you found some of it interesting. Thanks for reading and until next one