I'm trying to block my CLI app from exiting so people have enough time to read it

I learned about misusing the code
io::stdin().read_line(&mut String::new()).unwrap();

to make it wait for an input but I'm having issues on implementing it in my block of code in main.rs or cli.rs, everywhere I place it, it seems to either block modules or tells me it expects an identifier, any help on where it would go to block console from exiting?

        use structopt::StructOpt;

          mod cli;

        mod task;

      use cli::{Action::*, CommandLineArgs};

       use task::Task;

        io::stdin::read_line.&mut String::new().unwrap();

                  fn main() {

         // Get the command-line arguments.

          let CommandLineArgs {

                      action,

                    todo_file,

           } = CommandLineArgs::from_args();

          // Unpack the todo file.

            let todo_file = todo_file.expect("Failed to find todo file");

          // Perform the action.

           match  action {

         Add { text } => task::add_task(todo_file, Task::new(text)),

      List => task::list_tasks(todo_file), 

       Done { position } => task::complete_task(todo_file, position),

   

      }

      .expect("Failed to perform action")

Huh, your code as you have put is hard to read; here is the same code with some formatting applied to it (if you go to the Rust Playground, among the Tools on the top-right, there is rustfmt),

  • I have also used the code highlighting capabilities of this forum.

    In order to do that, use a pair of triple backquotes ``` to define the code block, and write the flavour of highlighting you wish after the first set of triple backquotes (e.g., rust, Python, bash, text (for no highlighting) etc.; if nothing is provided, it defaults to rust:

    ```rust
    // your code here:
    use structopt::StructOpt;

    mod cli;
    ```

    yields:

    // your code here:
    use structopt::StructOpt;
    
    mod cli;
    

use structopt::StructOpt;

mod cli;

mod task;

use cli::{Action::*, CommandLineArgs};

use task::Task;

io::stdin::read_line.&mut String::new().unwrap(); // <- ?

fn main() {

    // Get the command-line arguments.

    let CommandLineArgs { action, todo_file } = CommandLineArgs::from_args();

    // Unpack the todo file.

    let todo_file = todo_file.expect("Failed to find todo file");

    // Perform the action.

    match action {
        Add { text } => task::add_task(todo_file, Task::new(text)),

        List => task::list_tasks(todo_file),

        Done { position } => task::complete_task(todo_file, position),
    }
    .expect("Failed to perform action")

At this point, we can see that you have written the

io::stdin::read_line.&mut String::new().unwrap();

line outside the main() function (and you have also syntactical errors).

This means that if you change your code to do:

- io::stdin::read_line.&mut String::new().unwrap();

  fn main() {
+     io::stdin().read_line(&mut String::new()).unwrap();

      // Get the command-line arguments.
      let CommandLineArgs {

it should, at the very least, compile. Now, this will pause the CLI at the beginning of the program, before it has had any chance to display anything, so you should rather put that at the end if you intend it to be used to keep the console visible (note that this won't run if the program panics).

Also, I think there are better alternatives to keeping the console output visible, which are mainly regarding how the user runs your program; so I don't think it is up to Rust to really improve this situation. Let's see what other users have to say about all this :slightly_smiling_face:

2 Likes

Thanks man ! I didn't know about the rust playground and the rust-fmt I will do that when I ask questions from now on and yeah I had it outside for the purpose of the question. So I managed to clean up all the errors and put the stdin code but it is still exiting sadly. I also made a print statement and It it is not displaying

                      use std::io::stdin;

                //    I put it at the end
            fn main()
           {
        println!("Press any key to continue");
       stdin().read_line(&mut String::new()).unwrap();
           }

The Rust Compiler takes no prisoners lol it's been a struggle for a rookie

as long as I can make it stay up for longer then half a second so it can show up in the cmd I will be happy haha I've spent 8 hours in this CLI I don't think im down or know how to refractor the code into a for loop that parses user input and reads it.

If you include the errors, that will likely help guide others on the forum. (Use a ```text block.)

I'm assuming from the mention of cmd that this is Windows. An alternative way to address this is to make a shortcut to your program that looks something like:

C:\WINNT\system32\CMD.EXE /K "C:\some\path\to\YourProgram"

(You may have to play with the paths.) The /K should prevent the cmd window from closing after the program finishes.

1 Like

Thank you for your input! I have tried the cmd/k command but I do wanna port it to another computer and it would be nice to not have them input that command every time since it can get quite lengthy with the commands and arguments

error: process didn't exit successfully: `target\debug\to-do-cli.exe` (exit code: 1)

This could be the reason why it exits in half a second?

This is my first program in Rust so forgive me If I sound like a total rookie I'm just trying to get it released into something useable.

Right now it just flashes on my screen :frowning:

Does this mean you are double-clicking the exe file? So what is flashing up is the cmd window, then it hits an error or finishes, and vanishes.

I suggest making it work first when you run the command from within a cmd window. i.e. open cmd, run your cli-executable directly there, and see what happens. You should see the text printed, and be able to wait for input. i.e. if you try that simplest program you posted:

use std::io::stdin;

//    I put it at the end
fn main() {
    println!("Press any key to continue");
    stdin().read_line(&mut String::new()).unwrap();
}

On Linux I saved that in a file "cli.rs" and:

$ rustc cli.rs
$ ls -l
total 9636
-rwxrwxr-x 1 peter peter 3342432 Mar 27 01:30 cli
-rw-rw-r-- 1 peter peter     156 Mar 27 01:30 cli.rs
$ ./cli
Press any key to continue

and it dutifully waits. I can type a few things, and it stops when I press enter. On windows you'll have a cli.exe, and you don't have to compile from the cmd window, but something like this should work.

(You could do the same in cargo, making this the whole contents of main.rs.)

That you get an error suggests you have something wrong in the program that makes it exit before reaching read_line. Maybe you could first get this simple program working, and then work backwards?

1 Like

Let me know if you get the same error

We won't be able to compile this without cli.rs or task.rs. Please either add these files to the paste, or inline these modules into main.rs (i.e. replace mod cli; with mod cli { /* contents or cli.rs */ }, and similarly for mod task).

1 Like

Sorry I thought I had pasted the link to them

Thanks for helping! I feel like its such a simple error I just can't figure it out why it's exiting unsuccessfully

OK, so you're using structopt to parse command-line arguments, but invoke the program without passing them. Hence, your program probably exits with error after call to CommandLineArgs::from_args().

1 Like

So how do I fix it? Do I have to move code around?

Well, if you want the application not to exit if it's used incorrectly, you have to handle the error yourself, not relying on structopt exiting implicitly. There's StructOpt::from_args_safe, which returns Result, which you can then match on and do anything you want in case of error, including waitng for user input.

Why don't you just run your program from the command line? Open a cmd prompt, change to the directory of your project, and cargo run there. It's much easier to do than trying to work around GUI shenanigans in a manner that will only be annoying on other platforms.

Hahaha, I understand your frustration , the reason why was that I wanted to port it .exe file and all to my friends who don't have rust installed and since rust to my understanding makes a single binary file with all the dependencies on it. It would be awesome to port it out. Commands like cargo run need to have rustup installed right? I honestly did not think it would be this hard and difficult since fighting with the compiler for 8 hours seemed like the hard part. There really is no GUI shenanigans cargo build --release should run just as well as cargo run using the cmd start to-do-cli.exe right? but it doesn't I get a blank space.

You don't need to have the Rust toolchain installed in order to start your program. I suggested you use cargo run because then you don't have to manually find the right path and the weird, long name for the binary that the Rust compiler generates. But you absolutely can fish it out from the target/debug or target/release subdirectory of your project, and invoke it directly. You can also send the file to others who can in turn do the same.

Right , but you need it to use cargo run right? and yeah it's a good suggestion that's what I use too. But they don't have Rust installed can they still use cargo run? Btw don't take this the wrong way I just started with Rust this is literally my first project lol if they can use cargo run then I'll leave it how it is cause it seems to work fine with that command. I can't figure out why it exits so quickly with start file.exe and prints out nicely with cargo run albeit with the error: process and so far I've tried to slow it down and failed. It's a tough start for me boys lol. I'm happy it works though

Btw thanks for all your help! I'm limited in my coding knowledge of rust and have appreciated the input I got! I do not know about handing my own error code with StructOpt or how I would implement that. I honestly didn't think slowing down the program would be this hard lol I should've known

After the program is built, cargo run is equal to path/to/program.exe. So, no, they won't be able to use cargo run, but they don't have to.

It exits in the same way in both cases - with error like "no argument specified", if I understand your code correctly. But, if you run it from the CMD, be it cargo run or path/to/program.exe, the CMD itself still works, and all the messages remain; and if you execute the program in any other way, it opens CMD itself, which is closed when the program finishes.

As I said above, you have to use from_args_safe instead of from_args.

No and they don't need to. Did you read what I wrote? Here:

I.e. you change to the directory where the .exe resides, and invoke it by simply typing its name. You don't need cargo run for that, nor do others.