Reading 2 numbers from command line

I need to read at least 1, sometimes 2 numbers from command line, eg:

echo 2 | ./input or echo 2 5 | ./input

For 1 input number in Rust I did this that works:

  let mut val = String::new();
  std::io::stdin().read_line (&mut val).expect("Failed to read line");
  let val: usize = val.trim().parse().expect("Please type a number!");

  println!("{} + 3 = {}", val, val + 3);

Again, I always will read 1 input number, and sometimes 2 input numbers.

Here's how I did it in D:

  ulong[] x;
  foreach (_; 0 .. 2) { ulong a; readf!" %d"(a); x ~= a; }
  end_num = max(x[0], 3);
  start_num = max(x[1], 3);
  if (start_num > end_num) swap(start_num, end_num)

Here's how I did it in Nim:

  let x = stdin.readline.split
  end_num = max(x[0].parseUInt, 3'u)
  start_num = if x.len > 1: max(x[1].parseUInt, 3'u) else: 3'u
  if start_num > end_num: swap start_num, end_num

How do I do the equivalent in Rust?

Here's how I would unconditionally parse two numbers out of a whitespace-separated string:

let mut substr_iter = val.split_whitespace();
let mut next_num = || -> usize {
    substr_iter.next().expect("Not enough input numbers")
               .parse().expect("Input is not a number")
};
let val1 = next_num();
let val2 = next_num();

Playground

If you may or may not expect a second number depending on the value of the first one, then just don't call next_num() twice and you'll be fine since it's all lazy.

If you want to check whether there's a second input and take action accordingly, then you'll need to refactor the closure so that it doesn't expect(), but just returns an Option or a Result and leaves it up to the caller to decide what should be done with it.

Running this code:

fn main() {
  let mut val = String::new();
  let mut substr_iter = val.split_whitespace();
  let mut next_num = || -> usize {
    let str = substr_iter.next().expect("Not enough input numbers")
                         .parse().expect("Input is not a number")
  };
  let val1 = next_num();
  let val2 = next_num();

  println!("val1 = {}, val2 = {}", val1, val2);
}

produces this compiler error using rustc 1.36.0 (a53f9df32 2019-07-03)

rustc input2.rs
error: expected one of `.`, `;`, `?`, or an operator, found `}`
 --> input3.rs:7:3
  |
6 |                          .parse().expect("Input is not a number")
  |                                                                  - expected one of `.`, `;`, `?`, or an operator here
7 |   };
  |   ^ unexpected token

error: aborting due to previous error

Yup, that was a code typo in my original untested from-memory version. It's fixed in the latest edit.

The edited code compiles but doesn't work from the command line.

fn main() {
  let val = String::new();
  let mut substr_iter = val.split_whitespace();
  let mut next_num = || -> usize {
      substr_iter.next().expect("Not enough input numbers")
                 .parse().expect("Input is not a number")
  };
  let val1 = next_num();
  let val2 = next_num();

  println!("val1 = {}, val2 = {}", val1, val2);
}

When run from command line it produces these errors.

$ echo 4 | ./input2
thread 'main' panicked at 'Not enough input numbers', src/libcore/option.rs:1036:5
note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace.

$ echo 4 7 | ./input2
thread 'main' panicked at 'Not enough input numbers', src/libcore/option.rs:1036:5
note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace.

or even

$ ./input2         
thread 'main' panicked at 'Not enough input numbers', src/libcore/option.rs:1036:5
note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace.

Note that you still need to read the string from the input, as you did previously.

It works for 2 input numbers but not for 1;

fn main() {
  let mut val = String::new();
  std::io::stdin().read_line (&mut val).expect("Failed to read line");
  let mut substr_iter = val.split_whitespace();
  let mut next_num = || -> usize {
      substr_iter.next().expect("Not enough input numbers")
                 .parse().expect("Input is not a number")
  };
  let val1 = next_num();
  let val2 = next_num();

  println!("val1 = {}, val2 = {}", val1, val2);
}
$ echo 4 79 | ./input2
val1 = 4, val2 = 79

$ echo 4  | ./input2 
thread 'main' panicked at 'Not enough input numbers', src/libcore/option.rs:1036:5
note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace.

This works for 1 but not 2 inputs.

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

fn main() {
    let mut values: Vec<usize> = Vec::new();
    for line in std::io::stdin().lock().lines() {
        let elem: usize = match line.unwrap().trim().parse() {
            Ok(num) => num,
            Err(_) => {
                eprintln!("Invalid value entered");
                continue;
            },
        };
        values.push(elem);
    }
    let val1 = values[0];
    println!("number of inputs  = {}", values.len());
    let val2 = if values.len() > 1 { values[1] } else { 3 };
    
    println!("val1 = {}, val2 = {}", val1, val2);
}
$ echo 5 | ./input2
number of inputs  = 1
val1 = 5, val2 = 3

$ echo 5 99 | ./input2
Invalid value entered
thread 'main' panicked at 'index out of bounds: the len is 0 but the index is 0', /rustc/a53f9df32fbb0b5f4382caaad8f1a46f36ea887c/src/libcore/slice/mod.rs:2695:10
note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace.

As I told you above, the way this next_num() closure is currently written, you shouldn't call it twice when you know that there will only be one input. Every call to this closure is an assertion that there is one more input to be read. Notice the next().expect(...) sequence in the closure, which queries the next item from the input iterator and panics if there's none...

Furthermore, your second approach cannot work, because trim() only removes whitespace at the front and the back of the input string, and cannot separate two space-separated values inside of a string. That's what the split_whitespace() iteration in my example is here for.

Why is something so simple to do in 4 or 5 lines of D|Nim code be so unsimple to do in Rust? :face_with_head_bandage:

fn main() {
  let mut val = String::new();
  std::io::stdin().read_line (&mut val).expect("Failed to read line");
  let mut substr_iter = val.split_whitespace();
  let mut next_or_default = |def| -> usize {
      substr_iter.next().unwrap_or(def)
                 .parse().expect("Input is not a number")
  };
  let val1 = next_or_default("3");
  let val2 = next_or_default("3");

  println!("val1 = {}, val2 = {}", val1, val2);
}

Why is something so simple to do in 4 or 5 lines of D|Nim code be so unsimple to do in Rust?

Because reading whitespace-separated integers from STDIN reeks of early 60s FORTRAN and we'd rather focus on making full-featured command line parsers easy.

Thank You! My faith in humanity (ok, Rust) is (partially) restored. :slightly_smiling_face:

Here's the full Rust equivalent to the D and Nim snippets.

fn main() {
  let mut val = String::new();
  std::io::stdin().read_line (&mut val).expect("Failed to read line");
  let mut substr_iter = val.split_whitespace();
  let mut next_or_default = |def| -> usize {
      substr_iter.next().unwrap_or(def)
                 .parse().expect("Input is not a number")
  };
  let mut end_num = next_or_default("3");
  let mut start_num = next_or_default("3");
  if start_num > end_num { std::mem::swap(&mut end_num, &mut start_num) }
    
  println!("start_num = {}, end_num = {}", start_num, end_num);
}

And some (desired) output.

$ echo 12 | ./input2 
start_num = 3, end_num = 12

$ echo 12 5 | ./input2
start_num = 5, end_num = 12

$ echo 12 50 | ./input2
start_num = 12, end_num = 50

What I'm about to say I know you've heard before, but I want to give feedback on how the experience of getting this to work makes me feel about Rust (and feelings are reality to the feeler).

I'm trying to learn Rust by translating already developed code that works. My primary language has been Ruby for over 12 years now, though I've been a professional programmer (i.e. I did it for work) for over 35 years now. Thus I know how to see code in one language and learn how to translate it into another, once I understand the paradigm of the new language.

You know Rust is hard to learn, which is why you have some of the best documentation of any language. You have one of the best language forums, as the help provided herein for this problem demonstrates. But it's still not enough.

But here's what I still feel after all is said and done.

I have no real understanding what each component of next_or_default is doing, or why it's needed. If I had to do something close, but just a little different, I don't feel comfortable I could write something I know would work out of the box. It's almost like performing magic. If you want to do this, wave your hands this way, if you want to do that, wave them another way.

I know sometimes people developing languages don't see how people trying to learn the language sees it. I mean, you know and understand the language, and things seem obvious to you, and you may not feel it's necessary to explain in gritty detail how/why things work in specific ways.

I would just encourage you to present more documentation to demonstrate how to perform basic and general standard programming tasks in Rust, sort of like Rosetta Code for Rust. How to do this, that, and the other in Rust, like for this example.

Again, I appreciate the immediate help I received from the forum for this issue. I'm making (another) attempt to learn enough Rust to use it for my projects. I get frustrated, I leave, I come back, repeat loop. Hopefully the "I leave" periods will get shorter as I feel more like I understand how to write Rust code.

For what it's worth: (not sure how much of this documentation you managed to find):

  • The next method comes from Iterator, documented here. Calling next directly on an iterator is actually fairly uncommon (usually one would put it in a for loop or call one of the helper methods on that page), but this is one of the places where it makes sense.
  • The next method returns an Option<T>. Option<T> is a T that may or may not be present:
    pub enum Option<T> { Some(T), None }
    
  • The fundamental operation provided by the language for dealing with enum types like Option is to use match to convert each case into a value of a common type. For instance,
    let s = match substr_iter.next() {
        Some(x) => x,
        None => def,
    };
    
    is the same as calling let s = substr_iter.next().unwrap_or(def).
  • Of course, most of the common ways of using match on an Option are already provided as helper methods. These methods, such as unwrap_or and expect that were used in this thread, are documented here.
  • What is the T in our Option<T>? Depends on the iterator. In this case, it is &str. The documentation of split_whitespace doesn't say this directly (it probably ought to), but we can find out because it returns a type called SplitWhitespace, whose documentation is here. If you look at that page, and click the [+] next to impl<'a> Iterator for SplitWhitespace<'a>, you finally see type Item = &'a str. (Kind of annoying, yes.)
  • str::parse is documented here. It returns a Result, which is how we do error-handling in rust (since parsing can fail if the string is not an integer). The methods of Result (many of which are similar to Option) are documented here.

On that last note: The most common thing to do with a Result is to propagate the error case upwards using the ? operator, letting the caller decide how to handle it. But given that main has no caller, the example code decided to handle it there with expect.

(also, because we used a closure, ? would have returned from that closure instead of from main, requiring the closure to return a Result, which would not have helped much)

This, or let s = substr_iter.next.unwrap_or_else(|| def)? I.e. is the unmatched branch calculated too (let's imagine that def is really some expression and not just a variable)?

We are indeed creating resources like that; the Rust Cookbook is one such effort. There isn't anyone actively maintaining it right now, though, and it hasn't gotten to the quality level we'd like it to be before promoting it more widely (for example, linking to it from the website).

1 Like

Below is the final code that does 100% what I want.
I need to use the std::cmp::max function to make inputs < 3 be floored at 3.

fn main() {
  let mut val = String::new();
  std::io::stdin().read_line (&mut val).expect("Failed to read line");
  let mut substr_iter = val.split_whitespace();
  let mut next_or_default = |def| -> usize {
      substr_iter.next().unwrap_or(def)
                 .parse().expect("Input is not a number")
  };
  let mut end_num = std::cmp::max(next_or_default("3"), 3);
  let mut start_num = std::cmp::max(next_or_default("3"), 3);
  if start_num > end_num { std::mem::swap(&mut end_num, &mut start_num) }
    
  println!("start_num = {}, end_num = {}", start_num, end_num);
}

Now below inputs give what I want.

$ echo 12 2 | ./input2 
start_num = 3, end_num = 12

$ echo 0 5 | ./input2
start_num = 3 end_num = 5

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.