Trait async_io::IoSafe

source ·
pub unsafe trait IoSafe { }
Expand description

Types whose I/O trait implementations do not drop the underlying I/O source.

The resource contained inside of the Async cannot be invalidated. This invalidation can happen if the inner resource (the TcpStream, UnixListener or other T) is moved out and dropped before the Async. Because of this, functions that grant mutable access to the inner type are unsafe, as there is no way to guarantee that the source won’t be dropped and a dangling handle won’t be left behind.

Unfortunately this extends to implementations of Read and Write. Since methods on those traits take &mut, there is no guarantee that the implementor of those traits won’t move the source out while the method is being run.

This trait is an antidote to this predicament. By implementing this trait, the user pledges that using any I/O traits won’t destroy the source. This way, Async can implement the async version of these I/O traits, like AsyncRead and AsyncWrite.

Safety

Any I/O trait implementations for this type must not drop the underlying I/O source. Traits affected by this trait include Read, Write, Seek and BufRead.

This trait is implemented by default on top of libstd types. In addition, it is implemented for immutable reference types, as it is impossible to invalidate any outstanding references while holding an immutable reference, even with interior mutability. As Rust’s current pinning system relies on similar guarantees, I believe that this approach is robust.

Implementations on Foreign Types§

source§

impl IoSafe for File

source§

impl IoSafe for Stderr

source§

impl IoSafe for StderrLock<'_>

source§

impl IoSafe for Stdin

source§

impl IoSafe for StdinLock<'_>

source§

impl IoSafe for Stdout

source§

impl IoSafe for StdoutLock<'_>

source§

impl IoSafe for TcpStream

source§

impl IoSafe for UnixStream

source§

impl IoSafe for ChildStderr

source§

impl IoSafe for ChildStdin

source§

impl IoSafe for ChildStdout

source§

impl<T: Clone + IoSafe + ?Sized> IoSafe for Cow<'_, T>

source§

impl<T: IoSafe + Read> IoSafe for BufReader<T>

source§

impl<T: IoSafe + Write> IoSafe for BufWriter<T>

source§

impl<T: IoSafe + Write> IoSafe for LineWriter<T>

source§

impl<T: IoSafe + ?Sized> IoSafe for &mut T

source§

impl<T: IoSafe + ?Sized> IoSafe for Box<T>

source§

impl<T: ?Sized> IoSafe for &T

Reference types can’t be mutated.

The worst thing that can happen is that external state is used to change what kind of pointer as_fd() returns. For instance:

use std::cell::Cell;
use std::net::TcpStream;
use std::os::unix::io::{AsFd, BorrowedFd};

struct Bar {
    flag: Cell<bool>,
    a: TcpStream,
    b: TcpStream
}

impl AsFd for Bar {
    fn as_fd(&self) -> BorrowedFd<'_> {
        if self.flag.replace(!self.flag.get()) {
            self.a.as_fd()
        } else {
            self.b.as_fd()
        }
    }
}

We solve this problem by only calling as_fd() once to get the original source. Implementations like this are considered buggy (but not unsound) and are thus not really supported by async-io.

Implementors§