Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ follows <https://www.conventionalcommits.org/en/v1.0.0/> to track changes.

[#17]: https://github.com/loichyan/dynify/pull/17

### Fixed

- Add `Send` and `Sync` bounds for `Buffered` ([#18])

[#18]: https://github.com/loichyan/dynify/pull/18

## [0.1.1] - 2025-08-28

The major update since the previous release is the introduction of the
Expand Down
10 changes: 8 additions & 2 deletions examples/async_sendable.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::future::Future;
use std::mem::MaybeUninit;

use dynify::PinDynify;
use dynify::Dynify;

#[trait_variant::make(Send)]
#[dynify::dynify]
Expand All @@ -9,7 +10,12 @@ trait Client {
}

async fn make_request(client: &(dyn Sync + DynClient)) {
client.request("http://magic/coffee/shop").pin_boxed().await;
let mut stack = [MaybeUninit::<u8>::uninit(); 16];
let mut heap = Vec::<MaybeUninit<u8>>::new();
client
.request("http://magic/coffee/shop")
.init2(&mut stack, &mut heap)
.await;
}

fn poll_future(fut: impl Send + Future<Output = ()>) {
Expand Down
17 changes: 11 additions & 6 deletions src/container.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ pub unsafe trait PinEmplace<T: ?Sized>: Emplace<T> {
///
/// **Tips**: `Buffered<T: Future>` implements `Future`, so you can simply write
/// `async_hello().init(&mut stack).await` in practice.
pub struct Buffered<'a, T: ?Sized>(NonNull<T>, PhantomData<&'a mut [u8]>);
pub struct Buffered<'a, T: ?Sized>(NonNull<T>, PhantomData<&'a mut T>);
impl<'a, T: ?Sized> Buffered<'a, T> {
/// Constructs a new instance with the provided pointer.
///
Expand Down Expand Up @@ -214,6 +214,11 @@ impl<'a> Buffered<'a, dyn Any + Send + Sync> {
}
}

// SAFETY: Since we hold a exclusive reference to `T`, it's okay to inherit the
// `Send` and `Sync` bounds.
unsafe impl<T: ?Sized + Send> Send for Buffered<'_, T> {}
unsafe impl<T: ?Sized + Sync> Sync for Buffered<'_, T> {}

// Pretend `Buffered` owns the value of `T` rather than just a pointer to it.
// This, along with the `Buffered::project*` APIs, makes it easy to obtain a
// pinned reference to `T` in safe Rust. But the downside is that this prevents
Expand Down Expand Up @@ -275,7 +280,7 @@ impl fmt::Display for OutOfCapacity {
}
}

unsafe impl<'a, T: ?Sized, const N: usize> Emplace<T> for &'a mut MaybeUninit<[u8; N]> {
unsafe impl<'a, T: 'a + ?Sized, const N: usize> Emplace<T> for &'a mut MaybeUninit<[u8; N]> {
type Ptr = Buffered<'a, T>;
type Err = OutOfCapacity;

Expand All @@ -287,7 +292,7 @@ unsafe impl<'a, T: ?Sized, const N: usize> Emplace<T> for &'a mut MaybeUninit<[u
uninit_slice.emplace(constructor)
}
}
unsafe impl<'a, T: ?Sized, const N: usize> Emplace<T> for &'a mut [MaybeUninit<u8>; N] {
unsafe impl<'a, T: 'a + ?Sized, const N: usize> Emplace<T> for &'a mut [MaybeUninit<u8>; N] {
type Ptr = Buffered<'a, T>;
type Err = OutOfCapacity;

Expand All @@ -298,7 +303,7 @@ unsafe impl<'a, T: ?Sized, const N: usize> Emplace<T> for &'a mut [MaybeUninit<u
self.as_mut_slice().emplace(constructor)
}
}
unsafe impl<'a, T: ?Sized> Emplace<T> for &'a mut [MaybeUninit<u8>] {
unsafe impl<'a, T: 'a + ?Sized> Emplace<T> for &'a mut [MaybeUninit<u8>] {
type Ptr = Buffered<'a, T>;
type Err = OutOfCapacity;

Expand Down Expand Up @@ -393,7 +398,7 @@ mod __alloc {

// TODO: pinned vector?
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
unsafe impl<'a, T: ?Sized> Emplace<T> for &'a mut Vec<MaybeUninit<u8>> {
unsafe impl<'a, T: 'a + ?Sized> Emplace<T> for &'a mut Vec<MaybeUninit<u8>> {
type Ptr = Buffered<'a, T>;
type Err = Infallible;

Expand Down Expand Up @@ -446,7 +451,7 @@ mod __smallvec {
unsafe impl<'a, A, T> Emplace<T> for &'a mut SmallVec<A>
where
A: Array<Item = MaybeUninit<u8>>,
T: ?Sized,
T: 'a + ?Sized,
{
type Ptr = Buffered<'a, T>;
type Err = Infallible;
Expand Down
18 changes: 18 additions & 0 deletions src/container_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,24 @@ fn unpin_buffered() {
let _: &mut Buffered<usize> = Pin::into_inner(val);
}

#[test]
fn send_buffered() {
let mut stack = newstk::<16>();
let init = from_closure(|slot| slot.write(123));
let val: Buffered<usize> = init.init(&mut stack);
fn ensure_send(_: impl Send) {}
ensure_send(val);
}

#[test]
fn sync_buffered() {
let mut stack = newstk::<16>();
let init = from_closure(|slot| slot.write(123));
let val: Buffered<usize> = init.init(&mut stack);
fn ensure_sync(_: impl Sync) {}
ensure_sync(val);
}

#[test]
fn project_buffered() {
let mut stack = newstk::<16>();
Expand Down
7 changes: 5 additions & 2 deletions src/dynify.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,8 @@ illustrated below, you can combine `#[dynify]` with
[trait-variant](https://crates.io/crates/trait-variant) to achieve this:

```rust
# use dynify::{PinDynify, dynify};
# use dynify::{Dynify, dynify};
# use std::mem::MaybeUninit;
// You can also use `#(make(SendClient: Send))`. However, in this case, you can
// no longer specify a name in `#[dynify]` because `#[trait_variant::make]`
// will generate two traits, which leads to conflicting trait definitions.
Expand All @@ -133,8 +134,10 @@ trait Client {
fn run_client(
client: &(dyn DynClient + Sync),
) -> impl '_ + std::future::Future<Output = ()> + Send {
let mut stack = [MaybeUninit::<u8>::uninit(); 16];
let mut heap = Vec::<MaybeUninit<u8>>::new();
async move {
client.request("http://magic/request").pin_boxed().await;
client.request("http://magic/request").init2(&mut stack, &mut heap).await;
}
}
```
Expand Down