Pipes: fatal runtime error

I am writing a small application that spawns a child process and receives the response through the pipe created. I am getting the data back but I am seeing a fatal runtime error because of double free of fd. I am using "interprocess" crate for this. I am seeing similar issue with "os_pipe" crate too. Below is the code snippet. After exiting the function recv_till_end, rx_pipe is dropped and as part of that I see the error. Please help. Thanks

Error:
fatal runtime error: IO Safety violation: owned file descriptor already closed

Code Snippet:
use interprocess::unnamed_pipe::pipe;
use interprocess::unnamed_pipe::{Recver, Sender};
use std::io;
use std::io::{Read, Write};
use std::os::fd::{AsRawFd, RawFd};
use std::io::Error;
use std::io::ErrorKind;
use std::os::fd::FromRawFd;
use std::process::Stdio;

pub struct Pipe {
    txer: Sender,
    rxer: Recver,
}


impl Pipe {
    pub fn new() -> Self {
        let (txer, rxer) = pipe().unwrap();
        let p = Pipe {
            txer: txer,
            rxer: rxer,
        };
        println!(
            "Pipe::new() - txer: {:?}, rxer: {:?}",
            p.txer.as_raw_fd(),
            p.rxer.as_raw_fd()
        );
        p
    }

    pub fn tx_raw_fd(&self) -> Stdio {
        unsafe { Stdio::from_raw_fd(self.txer.as_raw_fd()) }
    }

    pub fn rx_raw_fd(&self) -> Stdio {
        unsafe { Stdio::from_raw_fd(self.rxer).as_raw_fd()) }
    }
}

Usage:

    async fn recv_till_end(&mut self) -> Result<(), Error> {
        let mut rx_pipe = Pipe::new();

        let mut cmd = Command::new(self.params.exe_path.as_ref().unwrap());
        cmd.stdout(rx_pipe.tx_raw_fd());
        let mut child = cmd.spawn()?;
        drop(cmd); // Release the Command instance since we have the Child


        let data = rx_pipe.rx_to_end(1024)?;
        println!("recv_till_end data len: {}", data.len());
        child.wait()?;
        Ok(())
    }

The unsafe parts are problematic, because the standard library requires an owned file descriptor. Clicking through the "owned file descriptor" link on that page, ownership of I/O devices are mutually exclusive.

I suspect the standard library is detecting this when your wrapper type drops, as the Stdio instances you created already handled the drop/close for those file descriptors.

It's somewhat similar to dubious double-ownership with a Vec:

Though instead of the "loose file descriptor" functions you use, the Vec::into_raw_parts takes ownership ("forgets") the Vec, so the example has to rebuild it.

Thanks for your help. I removed the unsafe usage and I am using a different crate "os_pipe" that provides a better abstraction. Now it looks better. Thanks