Repository: tokio-rs/tokio (language: rust)

Issue:
process: document how to read output from `Child`
tokio v1.41.0
ubuntu linux 22.04

**Description**

Child::wait_with_output() should take `&mut self` instead of `self`.    Taking `self` means it cannot be used in a `select!()`, which means there is no good way to simultaneously wait for a child processes output and a kill/cancel channel message.

stated differently, the docs for Child::kill() provide an example of using `select!()` with branches for Child::wait() and a oneshot channel recv().  This example works perfectly for Child::wait() but fails to compile for Child::wait_with_output().

The only way I was able to get it to work was to copy/paste the impl of Child::wait_with_output() into my own code and modify it to take `&mut self` instead.

See discussion #7132 with playground examples.




Discussion / hints:
This function cannot be changed due to backwards compatibility.
that sounds like a good reason to never change anything.
Not making changes that break existing code is absolutely fundamental to maintain a library. We can add new methods, but we cannot delete methods or change their signature.
Let's reframe as a question then:

how can an application use the tokio library to await output of a child process, and simultaneously listen for a message to kill that process?    The example given for Child::kill() does not read child's output, and does not compile if Child::wait_with_output() is substituted for Child::wait().

If a simple example can be created with present API, then let's add it to the Child::kill() docs, and this can be closed.   Otherwise, it seems some change (or addition) to the API must be needed.    no?
Reading the child's output is of course possible, and I'm happy to see such an example. Unfortunately, I don't have time to write it right now.

Fix patch:
diff --git a/tokio/src/process/mod.rs b/tokio/src/process/mod.rs
index 565795ac4e6..c59815b898f 100644
--- a/tokio/src/process/mod.rs
+++ b/tokio/src/process/mod.rs
@@ -1169,6 +1169,8 @@ impl Child {
     ///
     /// This is equivalent to sending a `SIGKILL` on unix platforms.
     ///
+    /// # Examples
+    ///
     /// If the child has to be killed remotely, it is possible to do it using
     /// a combination of the select! macro and a `oneshot` channel. In the following
     /// example, the child will run until completion unless a message is sent on
@@ -1190,6 +1192,46 @@ impl Child {
     ///     }
     /// }
     /// ```
+    ///
+    /// You can also interact with the child's standard I/O. For example, you can
+    /// read its stdout while waiting for it to exit.
+    ///
+    /// ```no_run
+    /// # use std::process::Stdio;
+    /// #
+    /// # use tokio::io::AsyncReadExt;
+    /// # use tokio::process::Command;
+    /// # use tokio::sync::oneshot::channel;
+    ///
+    /// #[tokio::main]
+    /// async fn main() {
+    ///     let (_tx, rx) = channel::<()>();
+    ///
+    ///     let mut child = Command::new("echo")
+    ///         .arg("Hello World!")
+    ///         .stdout(Stdio::piped())
+    ///         .spawn()
+    ///         .unwrap();
+    ///
+    ///     let mut stdout = child.stdout.take().expect("stdout is not captured");
+    ///
+    ///     let read_stdout = tokio::spawn(async move {
+    ///         let mut buff = Vec::new();
+    ///         let _ = stdout.read_to_end(&mut buff).await;
+    ///
+    ///         buff
+    ///     });
+    ///
+    ///     tokio::select! {
+    ///         _ = child.wait() => {}
+    ///         _ = rx => { child.kill().await.expect("kill failed") },
+    ///     }
+    ///
+    ///     let buff = read_stdout.await.unwrap();
+    ///
+    ///     assert_eq!(buff, b"Hello World!\n");
+    /// }
+    /// ```
     pub async fn kill(&mut self) -> io::Result<()> {
         self.start_kill()?;
         self.wait().await?;


Test patch: