How to pause the execution of the program?

Considering this code

fn main() {
    println!("This is a line");

    pause();

    println!("This is another line");
}

fn pause() {
    println!("Pausing! Press enter to continue...");

    let mut buffer = String::new();

    std::io::stdin()
        .read_line(&mut buffer)
        .expect("Failed to read line");
}

How can i print the file name, the line number and pause the program execution in line 4 where i called the pause() function ?

I want the pause function to pause the program execution and print something like this:
[src/main.rs:4] Pausing! Press enter to continue...

You could use log and env_logger for this:

use log::{info, LevelFilter};

use std::io::Write;

fn main() {
    env_logger::builder()
        .filter_level(LevelFilter::Info)
        .format(|buf, record| {
            writeln!(
                buf,
                "[{}:{}] {}",
                record.file().unwrap(),
                record.line().unwrap(),
                record.args()
            )
        })
        .init();

    info!("This is a line");

    pause();

    info!("This is another line");
}

fn pause() {
    info!("Pausing! Press enter to continue...");
}

Playground.


As for the pause, isn't Stdin::read_line pausing? :thinking: According to the docs:

Give it text interactively by running the executable directly, in which case it will wait for the Enter key to be pressed before continuing

I don't understand how this pauses execution?

Isn't there something in the standard library to do that ? All this code just to pause the program and type a message that has some info !!

There are 2 problems with this:

fn main() {
    println!("This is a line");

    pause();

    println!("This is another line");
}

fn pause() {
    dbg!("Pausing! Press enter to continue...");

    let mut buffer = String::new();

    std::io::stdin()
        .read_line(&mut buffer)
        .expect("Failed to read line");
}

Output:

This is a line
[src/bin/test.rs:10] "Pausing! Press enter to continue..." = "Pausing! Press enter to continue..."

This is another line
  1. The file name and the line number is where the dbg!() where called (line 10), not where the pause() function called. I want the line where the pause() been called.
  2. The output is super ugly, instead of this:
[src/bin/test.rs:10] "Pausing! Press enter to continue..." = "Pausing! Press enter to continue..."

i want this:
[src/bin/test.rs:4] Pausing! Press enter to continue...

You may be interested in this previous thread:

I do believe

    std::io::stdin()
        .read_line(&mut buffer)
        .expect("Failed to read line");

is good enough of a pause, but maybe there are libraries out there that are easier to use?

This is probably best handled by converting your function to a macro. Macros expand where they are called:

use log::{info, LevelFilter};

use std::io::Write;

macro_rules! pause {
    () => {
        {
            info!("Pausing! Press enter to continue...");
            
            let mut buffer = String::new();
            
            std::io::stdin()
                .read_line(&mut buffer)
                .expect("Failed to read line");
        }
    };
}

fn main() {
    env_logger::builder()
        .filter_level(LevelFilter::Info)
        .format(|buf, record| {
            writeln!(
                buf,
                "[{}:{}] {}",
                record.file().unwrap(),
                record.line().unwrap(),
                record.args()
            )
        })
        .init();

    info!("This is a line");

    pause!();

    info!("This is another line");
    
    pause!()
}

Playground.

Stderr:

[src/main.rs:33] This is a line
[src/main.rs:35] Pausing! Press enter to continue...
[src/main.rs:37] This is another line
[src/main.rs:39] Pausing! Press enter to continue...

(notice that the line number in the log messages for the pauses is now equal to the lines where we used pause!)

This works super fine:

#[macro_export]
macro_rules! pause {
    () => {
        println!(
            "[{}:{}] Pausing! Press enter to continue...",
            file!(),
            line!()
        );

        let mut buffer = String::new();

        std::io::stdin()
            .read_line(&mut buffer)
            .expect("Failed to read line");
    };
}

Thanks for the inspiration to use macros

One problem is that the autocompletion in vscode does not work inside the macro !!

macro_rules!  {
    () => {
        
    };
}

Nice, I didn't know about file!, line! and column! macros. This is even better as it avoids the dependency on the log and env_logger crates in your case.

Can't help you with that, unfortunately. There's a category "Editors and IDEs" here on URLO, maybe open a new topic there?

You can use #[track_caller]

fn main() {
    println!("This is a line");

    pause();

    println!("This is another line");
}

#[track_caller]
fn pause() {
    let caller = std::panic::Location::caller();
    println!(
        "[{}: {}] Pausing! Press enter to continue...",
        caller.file(),
        caller.line()
    );

    let mut buffer = String::new();

    std::io::stdin()
        .read_line(&mut buffer)
        .expect("Failed to read line");
}
4 Likes

Problem with this solution is that it does not print the file name and the line number where the function was called.

src/lib.rs:

#[macro_export]
macro_rules! pause {
    () => {
        println!(
            "[{}:{}] Pausing! Press enter to continue...",
            file!(),
            line!()
        );

        let mut buffer = String::new();

        std::io::stdin()
            .read_line(&mut buffer)
            .expect("Failed to read line");
    };
}

pub fn pause() {
    let caller = std::panic::Location::caller();

    println!(
        "[{}:{}] Pausing! Press enter to continue...",
        caller.file(),
        caller.line(),
    );

    let mut buffer = String::new();

    std::io::stdin()
        .read_line(&mut buffer)
        .expect("Failed to read line");
}

src/bin/test.rs:

fn main() {
    println!("This is a line");

    beginning_rust::pause!();
    beginning_rust::pause();

    println!("This is another line");
}

output:

This is a line
[src/bin/test.rs:4] Pausing! Press enter to continue...

[src/lib.rs:21] Pausing! Press enter to continue...

This is another line

You need the #[track_caller] at the definition of pub fn pause() to make Rust track the caller's location, and not the line on which you call let caller = std::panic::Location::caller();

See Rust Playground - the version of fn pause I've called tracked_pause is the one you want, and reproduced below:

// NB: The `track_caller` attribute is necessary here - without it,
// caller.file() and caller.line()` are going to be in `fn pause()`, not
// the caller's location.
#[track_caller]
pub fn pause() {
    let caller = std::panic::Location::caller();

    println!(
        "[{}:{}] Pausing! Press enter to continue...",
        caller.file(),
        caller.line(),
    );

    let mut buffer = String::new();

    std::io::stdin()
        .read_line(&mut buffer)
        .expect("Failed to read line");
}
1 Like

Thanks a lot, i forgot #[track_caller], now it works fine.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.