Skip to content

Commit

Permalink
Implement Future for SmallBox<F> where F: Future (#39)
Browse files Browse the repository at this point in the history
* run cargo fmt

* implement Future for SmallBox

* add unit test for SmallBox<Future>

* replace pollster with futures crate for future test
  • Loading branch information
PonasKovas authored Dec 23, 2024
1 parent eb4dfc3 commit edd5464
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 13 deletions.
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,6 @@ default = ["std"]
std = []
coerce = []
nightly = ["coerce"]

[dev-dependencies]
futures = { version = "0.3", default-features = false, features = ["executor"] }
4 changes: 2 additions & 2 deletions benches/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
extern crate smallbox;
extern crate test;

use smallbox::space::*;
use smallbox::SmallBox;
use test::black_box;
use smallbox::space::*;
use test::Bencher;
use test::black_box;

#[bench]
fn smallbox_small_item_small_space(b: &mut Bencher) {
Expand Down
8 changes: 4 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@
//! Eliminate heap alloction for small items by `SmallBox`:
//!
//! ```rust
//! use smallbox::space::S4;
//! use smallbox::SmallBox;
//! use smallbox::space::S4;
//!
//! let small: SmallBox<_, S4> = SmallBox::new([0; 2]);
//! let large: SmallBox<_, S4> = SmallBox::new([0; 32]);
Expand All @@ -93,8 +93,8 @@
//! extern crate smallbox;
//!
//! # fn main() {
//! use smallbox::space::*;
//! use smallbox::SmallBox;
//! use smallbox::space::*;
//!
//! let array: SmallBox<[usize], S2> = smallbox!([0usize, 1]);
//!
Expand All @@ -108,8 +108,8 @@
//! ```rust
//! # #[cfg(feature = "coerce")]
//! # {
//! use smallbox::space::*;
//! use smallbox::SmallBox;
//! use smallbox::space::*;
//!
//! let array: SmallBox<[usize], S2> = SmallBox::new([0usize, 1]);
//!
Expand All @@ -127,8 +127,8 @@
//! # fn main() {
//! use std::any::Any;
//!
//! use smallbox::space::S2;
//! use smallbox::SmallBox;
//! use smallbox::space::S2;
//!
//! let num: SmallBox<dyn Any, S2> = smallbox!(1234u32);
//!
Expand Down
43 changes: 36 additions & 7 deletions src/smallbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use core::any::Any;
use core::cell::UnsafeCell;
use core::cmp::Ordering;
use core::fmt;
use core::future::Future;
use core::hash::Hash;
use core::hash::{self};
use core::marker::PhantomData;
Expand All @@ -13,6 +14,7 @@ use core::mem::{self};
use core::ops;
#[cfg(feature = "coerce")]
use core::ops::CoerceUnsized;
use core::pin::Pin;
use core::ptr;

use ::alloc::alloc;
Expand Down Expand Up @@ -42,8 +44,8 @@ impl<T: ?Sized + Unsize<U>, U: ?Sized, Space> CoerceUnsized<SmallBox<U, Space>>
/// extern crate smallbox;
///
/// # fn main() {
/// use smallbox::space::*;
/// use smallbox::SmallBox;
/// use smallbox::space::*;
///
/// let small: SmallBox<[usize], S4> = smallbox!([0usize; 2]);
/// let large: SmallBox<[usize], S4> = smallbox!([1usize; 8]);
Expand Down Expand Up @@ -85,8 +87,8 @@ impl<T: ?Sized, Space> SmallBox<T, Space> {
/// # Example
///
/// ```
/// use smallbox::space::*;
/// use smallbox::SmallBox;
/// use smallbox::space::*;
///
/// let small: SmallBox<_, S4> = SmallBox::new([0usize; 2]);
/// let large: SmallBox<_, S4> = SmallBox::new([1usize; 8]);
Expand Down Expand Up @@ -119,9 +121,9 @@ impl<T: ?Sized, Space> SmallBox<T, Space> {
/// # Example
///
/// ```
/// use smallbox::SmallBox;
/// use smallbox::space::S2;
/// use smallbox::space::S4;
/// use smallbox::SmallBox;
///
/// let s: SmallBox<_, S4> = SmallBox::new([0usize; 4]);
/// let m: SmallBox<_, S2> = s.resize();
Expand All @@ -148,8 +150,8 @@ impl<T: ?Sized, Space> SmallBox<T, Space> {
/// # Example
///
/// ```
/// use smallbox::space::S1;
/// use smallbox::SmallBox;
/// use smallbox::space::S1;
///
/// let stacked: SmallBox<usize, S1> = SmallBox::new(0usize);
/// assert!(!stacked.is_heap());
Expand Down Expand Up @@ -239,8 +241,8 @@ impl<T: ?Sized, Space> SmallBox<T, Space> {
///
/// # Examples
/// ```
/// use smallbox::space::S1;
/// use smallbox::SmallBox;
/// use smallbox::space::S1;
///
/// let stacked: SmallBox<_, S1> = SmallBox::new([21usize]);
/// let val = stacked.into_inner();
Expand Down Expand Up @@ -280,8 +282,8 @@ impl<Space> SmallBox<dyn Any, Space> {
/// # fn main() {
/// use core::any::Any;
///
/// use smallbox::space::*;
/// use smallbox::SmallBox;
/// use smallbox::space::*;
///
/// fn print_if_string(value: SmallBox<dyn Any, S1>) {
/// if let Ok(string) = value.downcast::<String>() {
Expand Down Expand Up @@ -318,8 +320,8 @@ impl<Space> SmallBox<dyn Any + Send, Space> {
/// # fn main() {
/// use core::any::Any;
///
/// use smallbox::space::*;
/// use smallbox::SmallBox;
/// use smallbox::space::*;
///
/// fn print_if_string(value: SmallBox<dyn Any, S1>) {
/// if let Ok(string) = value.downcast::<String>() {
Expand Down Expand Up @@ -438,6 +440,26 @@ impl<T: ?Sized + Hash, Space> Hash for SmallBox<T, Space> {
}
}

// We can implement Future for SmallBox soundly, even though it's not implemented for the std Box
// The reason why it's not implemented for std Box is only because Box<T>: Unpin unconditionally,
// even when T: !Unpin, which always allows to get &mut Box<T> from Pin<&mut Box<T>>.
// For SmallBox, this is not the case, because it might carry the data on the stack, so if T: !Unpin,
// SmallBox<T>: !Unpin also. That means you can't get &mut SmallBox<T> from Pin<&mut SmallBox<T>>
// in safe code, so it's safe to implement Future for SmallBox directly.
impl<F: Future + ?Sized, S> Future for SmallBox<F, S> {
type Output = F::Output;

fn poll(
self: Pin<&mut Self>,
cx: &mut core::task::Context<'_>,
) -> core::task::Poll<Self::Output> {
// Safety: when the SmallBox is pinned, the data on the stack is pinned
// The data on the heap is also pinned naturally, and `F` is innacessible in safe code,
// so all Pin guarantees are satisfied.
unsafe { Pin::new_unchecked(&mut **self.get_unchecked_mut()) }.poll(cx)
}
}

unsafe impl<T: ?Sized + Send, Space> Send for SmallBox<T, Space> {}
unsafe impl<T: ?Sized + Sync, Space> Sync for SmallBox<T, Space> {}

Expand Down Expand Up @@ -666,6 +688,13 @@ mod tests {
assert_eq!(cellbox.get(), 1);
}

#[test]
fn test_future() {
let boxed_fut: SmallBox<_, S1> = SmallBox::new(async { 123 });

assert_eq!(futures::executor::block_on(boxed_fut), 123);
}

#[test]
fn test_variance() {
#[allow(dead_code)]
Expand Down

0 comments on commit edd5464

Please sign in to comment.