Skip to content
CryoCryo home
Stdlibprocess

child

import std::process::child; · source

Child

type struct Child {
    stdin:  Option<ChildStdin>;
    stdout: Option<ChildStdout>;
    stderr: Option<ChildStderr>;

    static new(pid: i32, handle: u64, stdin: Option<ChildStdin>, stdout: Option<ChildStdout>, stderr: Option<ChildStderr>) -> Child;
    id(&this) -> i32;
    wait(mut &this) -> Result<ExitStatus, IoError>;
    async join(mut &this) -> Result<ExitStatus, IoError>;
    try_wait(mut &this) -> Result<Option<ExitStatus>, IoError>;
    kill(&this) -> Result<(), IoError>;
    send_signal(&this, sig: Signal) -> Result<(), IoError>;
}

A running subprocess, produced by spawn. It holds the pid (private, behind id()) and whichever pipe ends you asked for.

MethodReturnsNotes
id()i32
wait()Result<ExitStatus, IoError>Blocks. Closes stdin first so the child sees EOF.
join()async ExitStatusThe non-blocking wait: parks on the reactor (a pidfd on Linux) or the blocking pool.
try_wait()Result<Option<ExitStatus>, IoError>Ok(None) means still running.
kill()Result<(), IoError>SIGKILL on POSIX. Does not wait.
send_signal(sig)Result<(), IoError>

wait closing stdin first is deliberate: it is deadlock-avoidant for a child that reads all of stdin before writing anything to stdout. join does not touch the pipes, so a child that writes more than a pipe buffer holds must have its stdout / stderr drained while you await — the same hazard, and the reason Command::collect drains first.

The pipe ends

type struct ChildStdin {
    fd: i32;

    static from_fd(raw_fd: i32) -> ChildStdin;
}

type struct ChildStdout {
    fd: i32;

    static from_fd(raw_fd: i32) -> ChildStdout;
}

type struct ChildStderr {
    fd: i32;

    static from_fd(raw_fd: i32) -> ChildStderr;
}

The parent's ends of the pipes a Stdio::Piped configuration created. ChildStdin implements Write; ChildStdout and ChildStderr implement Read. Dropping ChildStdin closes the pipe, which is what signals EOF to the child.

Reaping

Every Child should have wait — or kill then wait — called before it drops.

Child::drop closes any captured pipe ends and makes a non-blocking best-effort reap. If the child has already exited, its zombie is released there. If it is still running, a destructor cannot block, so the child stays detached and reaping it remains your responsibility. That closes the common "spawn, child finishes, drop without wait" leak without ever blocking. Cancelling a join mid-wait leaves the child in exactly this state.

Trait implementations

implement trait Drop for struct Child

implement trait Write for struct ChildStdin

implement trait Drop for struct ChildStdin

implement trait Read for struct ChildStdout

implement trait Drop for struct ChildStdout

implement trait Read for struct ChildStderr

implement trait Drop for struct ChildStderr

ExitStatus

type struct ExitStatus {
    code:     i32;
    signal:   i32;
    signaled: boolean;

    success(&this) -> boolean;
    exit_code(&this) -> Option<i32>;
    terminating_signal(&this) -> Option<i32>;
}

POSIX packs a normal exit and a signal death into one status word; ExitStatus is the decoded form.

MethodReturnsNotes
success()booleanExited with status 0.
exit_code()Option<i32>None if killed by a signal.
terminating_signal()Option<i32>Some if killed by a signal.