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§
impl IoSafe for File
impl IoSafe for Stderr
impl IoSafe for StderrLock<'_>
impl IoSafe for Stdin
impl IoSafe for StdinLock<'_>
impl IoSafe for Stdout
impl IoSafe for StdoutLock<'_>
impl IoSafe for TcpStream
impl IoSafe for UnixStream
impl IoSafe for ChildStderr
impl IoSafe for ChildStdin
impl IoSafe for ChildStdout
impl<T: Clone + IoSafe + ?Sized> IoSafe for Cow<'_, T>
impl<T: IoSafe + Read> IoSafe for BufReader<T>
impl<T: IoSafe + Write> IoSafe for BufWriter<T>
impl<T: IoSafe + Write> IoSafe for LineWriter<T>
impl<T: IoSafe + ?Sized> IoSafe for &mut T
impl<T: IoSafe + ?Sized> IoSafe for Box<T>
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
.