Search code examples
macosrustterminalstty

Why is there a trailing "D" of my println!() output?


I am following this reference to implement a simple program which calculates the minimum number of inputs:

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

fn read_vec() -> Vec<i32> {
    let mut vec: Vec<i32> = Vec::<i32>::new();

    let stdin = io::stdin();
    println!("Enter a list of numbers, one per line. End with Ctrl-D (Linux) or Ctrl-Z (Windows).");
    
    for line in stdin.lock().lines() {
        let line = line.unwrap();
        match line.trim().parse::<i32>()  {
            Ok(num) => vec.push(num),
            Err(_) => println!("What did I say about numbers?"),
        }
    }

    vec
}

pub enum SomethingOrNothing<T> {
    Something(T),
    Nothing,
}

pub use self::SomethingOrNothing::*;

type NumberOrNothing = SomethingOrNothing<i32>;

pub trait Minimum: Copy {
    fn min(self, b: Self) -> Self;
}

pub fn vec_min<T: Minimum>(v: Vec<T>) -> SomethingOrNothing<T> {
    let mut min = Nothing;
    for e in v {
        min = match min {
            Something(t) => Something(e.min(t)),
            Nothing => Something(e),
        }
    }
    min
}

impl Minimum for i32 {
    fn min (self, b: Self) -> Self {
        if self < b {self} else {b}
    }
}

impl NumberOrNothing {
    pub fn print(self) {
        match self {
            Nothing => println!("The number is: <nothing>"),
            Something(n) => println!("{}", n),
        };
    }
}

fn main() {
    let vec = read_vec();
    let min = vec_min(vec);
    min.print();
}

Build an run the program:

Enter a list of numbers, one per line. End with Ctrl-D (Linux) or Ctrl-Z (Windows).
100
8
200
8D

We can see there is a trailing "D" after the minimum number: 8. But if I changed the output from:

Something(n) => println!("{}", n),

to:

Something(n) => println!("The number is: {}", n),

The output seems normal:

Enter a list of numbers, one per line. End with Ctrl-D (Linux) or Ctrl-Z (Windows).
100
8
200
The number is: 8

I guess the issue is related to stdout buffer, but can't figure out why. Anyone can give some clues?

P.S., I can reproduce this issue on macOS(zsh) and OmniOS (bash), but can't reproduce on Linux(bash).


Solution

  • I was able to recreate this on macOS using iTerm (tested with zsh and bash). It looks like the problem is related to the the terminal echoing signal control commands:

    So to stop this, you'll want to turn off echoctl

    The easiest way to see what's happening is to hit Crtl-D while still on a line with a number:

    Enter a list of numbers, one per line. End with Ctrl-D (Linux) or Ctrl-Z (Windows).
    1
    2
    3
    4^D
    

    What's happening in your case is: you're on an empty line and hit Ctrol-D, the terminal echos ^D to the line and then moves the cursor back to the beginning of the line, then your programs output overwrites the ^D. So, if you output a single digit number, the ^ is overwritten but the D remains (as in your example). When you output more characters the ^ and the D are overwritten.

    Here's a demonstration where the D is overwritten:

    Enter a list of numbers, one per line. End with Ctrl-D (Linux) or Ctrl-Z (Windows).
    100
    200
    300
    100