# `unchecked_wrap`
This crate provides the `UncheckedSync<T>`, `UncheckedSend<T>`, and
`UncheckedSyncSend<T>` types. They are transparent wrappers around `T`, except
that they unconditionally implement `Sync`, `Send`, or both `Sync` and `Send`,
respectively. They can be used for, e.g., a `static mut` replacement, sending
pointers over thread boundaries, storing raw pointers in thread-safe
abstractions, etc.
Constructing these wrappers is unsafe. However, the `Unchecked` wrapper types
implement `Deref` and `DerefMut`, making them mostly usable as if they're just
plain values of type `T`.
`UncheckedSync<T>`, `UncheckedSend<T>`, and `UncheckedSyncSend<T>` are all
guaranteed to have the same size, alignment, and ABI as `T`.
## Examples
### A `static mut` replacement
Generally, global mutable state should be avoided. However, sometimes it is the
right tool. This crate makes this usage pattern more convenient.
```rust
mod my_module {
use std::cell::Cell;
use unchecked_wrap::UncheckedSync;
// SAFETY: Only accessed via functions in this module that should be
// used only in a single thread
static THING: UncheckedSync<Cell<u64>> = unsafe { UncheckedSync::new(Cell::new(0)) };
/// # Safety
/// Must be called in the same thread as other functions in this module.
pub unsafe fn increment() {
THING.set(THING.get() + 1);
}
/// # Safety
/// Must be called in the same thread as other functions in this module.
pub unsafe fn get() -> u64 {
THING.get()
}
}
// SAFETY: This doctest is single-threaded.
unsafe {
my_module::increment();
my_module::increment();
assert_eq!(my_module::get(), 2);
}
```
### Sending a raw pointer across thread boundaries
```rust
use unchecked_wrap::UncheckedSend;
let x = 123;
// Suppose that there's some reason that needs to be a raw pointer.
// SAFETY: A raw pointer doesn't actually have thread-safety invariants.
let ptr = unsafe { UncheckedSend::new(&raw const x) };
// SAFETY: `x` is not deallocated yet, and is not modified
assert_eq!(unsafe { **ptr }, 123);
});
scope.spawn(move || {
// SAFETY: `x` is not deallocated yet, and is not modified
assert_eq!(unsafe { **ptr }, 123);
});
});
```
### Storing a raw pointer in a thread-safe abstraction
```rust
use std::marker::PhantomData;
use std::ptr::NonNull;
use unchecked_wrap::UncheckedSyncSend;
struct MyBox<T> {
// We use UncheckedSyncSend to ignore the auto traits from NonNull,
// then we use PhantomData to get back the correct auto trait impls.
// That is, MyBox<T> implements Send/Sync iff T implements Send/Sync.
ptr: UncheckedSyncSend<NonNull<T>>,
_phantom: PhantomData<T>,
}
// No need for error-prone implementations of Send and Sync.
impl<T> MyBox<T> {
fn new(value: T) -> Self {
let ptr = NonNull::new(Box::into_raw(Box::new(value))).unwrap();
Self {
// SAFETY: A MyBox<T> is treated as if it owns T,
ptr: unsafe { UncheckedSyncSend::new(ptr) },
_phantom: PhantomData,
}
}
}
impl<T> Drop for MyBox<T> {
fn drop(&mut self) {
let ptr = *self.ptr;
// SAFETY: ptr was allocated via a Box
unsafe {
drop(Box::from_raw(ptr.as_ptr()));
}
}
}
let a_box = MyBox::new(String::from("abc"));
let join_handle = std::thread::spawn(move || {
drop(a_box);
});
join_handle.join().unwrap();
```
## A note on trait implementations
`UncheckedSync`, `UncheckedSend`, and `UncheckedSyncSend` do not implement
common traits such as `Debug` or `Hash`
This is because, if, for example, `UncheckedSync<T>` were to implement `Debug`
by forwarding to the `Debug` implementation of the `T` inside, and if a struct
were to store an `UncheckedSync`/`UncheckedSend`, and a `#[derive(Debug)]` were
to be applied to the struct, then the automatically-generated `Debug` impl might
read the `T` value inside in a way that violates thread-safety. (There was
previously [an unsoundness in std][repeat_n] due to a similar issue with
[`ManuallyDrop`][ManuallyDrop].) In order to avoid this footgun, the `Unchecked`
wrappers do not implement such traits at all.
However, `UncheckedSync<T>`, `UncheckedSend<T>`, and `UncheckedSyncSend<T>` each
implement `Copy` when `T: Copy`. This is to facilitate storing raw pointers in
contexts where they are known to be thread-safe, and then easily copy them
around. I believe that `Copy` is unlikely to be a footgun in the aforementioned
way.
[repeat_n]: https://github.com/rust-lang/rust/issues/130140
[ManuallyDrop]: https://doc.rust-lang.org/stable/std/mem/struct.ManuallyDrop.html#deriving-traits