I am playing with the following two code snippets
// Snippet1 in C #include <stdio.h> int main(){ FILE * fp = fopen("/dev/tty", "r"); int c = getc(fp); printf("%d", c); }
// Snippet2 in Rust use std::io::Read; use std::fs; fn main() { let mut f: fs::File = fs::File::open("/dev/tty").unwrap(); let mut buf: [u8;1] = [0]; f.read_exact(&mut buf).unwrap(); print!("byte: {}", buf[0]); }
What the above code wants to do is to read a byte from the user keyboard and then print it to stdout. The confusing thing is two snippets have different behaviors:
➜ playground gcc main.c -o main ➜ playground ./main a # input a and press Enter 97%
➜ playground cargo run -q a # input a and press Enter byte: 97% ➜ playground ➜ playground
I am sorry about the format of the above code, I don’t know how to place the prompt at the start of a new line:(
Please note, there are two shell prompts ➜ playground
after the execution of Rust code?
I am guessing the Enter
is sent to the input buffer as if I have pressed it after execution.
If I know what is actually happening in the buffer, I will find out the cause of this distinction, so I am wondering what’s going on there?
BTW, my environment:
➜ playground rustc --version rustc 1.57.0 (f1edd0429 2021-11-29) ➜ playground gcc --version gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0 Copyright (C) 2019 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
If my question is not allowed, feel free to ask me to delete it:) Thanks in advance:)
Advertisement
Answer
Note that in C, FILE*
and the corresponding functions are buffered, so the C program reads the key and newline characters and puts them in its buffer, whereas your Rust code doesn’t use buffering. As a consequence the newline character is still in the kernel buffer for the TTY when your Rust program finishes and so the newline is seen by your shell, whereas the newline has been removed from the kernel buffer by your C program.
You should get the same behaviour as your Rust program with this unbuffered C code:
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <unistd.h> int main() { int fd = open ("/dev/tty", O_RDONLY); char c; read (fd, &c, 1); printf("%d", c); }
or the same behaviour as your C program with this buffered Rust code:
use std::io::{ BufReader, Read }; use std::fs; fn main() { let mut f = BufReader::new (fs::File::open("/dev/tty").unwrap()); let mut buf: [u8;1] = [0]; f.read_exact(&mut buf).unwrap(); print!("byte: {}", buf[0]); }