Capture output of a continuously running child process?

Hey everyone,

I have a proxy binary that I run external to Rust. Once it's up and running, I pipe web requests through it so the binary never closes while my rust program is open. I want to start the binary from within my Rust application but since it takes a few seconds to spin up, I'm trying to monitor the output. Eventually I want to look for a specific string to come through then move on with the execution of the program.

My first attempt was here: Capturing Child Process Output V1 - Pastebin.com

This successfully spawns the child process but doesn't capture any of the output so I have no way of knowing if it started correctly.

My second attempt, after looking through the forums here, got a little closer: Capturing Child Process Output V2 - Pastebin.com

This one also successfully starts the child process and successfully grabs the output of the child process and looks for a matching string to indicate the proxy started correctly. I think I'm closer with this solution to what I want but I can't get it to exit the .for_each loop in the bufreader. I thought adding return; would do it but that doesn't appear to work in Rust and there doesn't appear to be a break; command either.

Can anyone tell me if I'm on the right track? How would I stop the bufreader operation after I match the string I'm looking for so the program can continue?

When you write foreach(|line| ... ) these two pipe symbols (|) create an closure. A closure is basically a local, anonymous function. See: Closures - The Rust Programming Language

So yes, you can't break that foreach. A solution would be a simple while loop instread:

    for line in output_reader.lines().filter_map(|line| line.ok()) {
        if check_if_finished(line) {
            return;
        }
    }
2 Likes

Awesome, thanks! This got me on the right track.

Since my function was returning a result, it wasn't enough to just return; from the for loop, I had to return Ok(()).

But overall the for loop was the way to let me break out. Thanks!

I agree that using simple for loop is the cleanest solution, but for the sake of completeness I must tell you that there is a version of for_each, which allows early returns - Iterator::try_for_each. Using ControlFlow you can control the control flow "around" passed closure. For example (snippet taken from std docs):

use std::ops::ControlFlow;

let r = (2..100).try_for_each(|x| {
    if 323 % x == 0 {
        return ControlFlow::Break(x)
    }

    ControlFlow::Continue(())
});
assert_eq!(r, ControlFlow::Break(17));

I'm not suggesting that you should use it. It would probably be more confusing than a normal loop. But I thought you might like to know there are alternatives. :smile:

5 Likes

In this case, return can be omitted:

let r = (2..100).try_for_each(|x| {
    if 323 % x == 0 {
        ControlFlow::Break(x)
    } else {
        ControlFlow::Continue(())
    }
});

:wink:

1 Like

That's awesome, thanks!

I'm just at the beginning of trying to learn Rust so all of these options are good to know.

1 Like

I love the extra info, thank you!

1 Like