unchecked_wrap 0.1.0

Convenient UncheckedSync/Send wrapper types
Documentation
# `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) };
std::thread::scope(|scope| {
    scope.spawn(move || {
        // 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