Skip to content

Commit 50656e7

Browse files
committed
Merge branch 'main'
2 parents 1a661e3 + 2f11596 commit 50656e7

File tree

5 files changed

+101
-5
lines changed

5 files changed

+101
-5
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ follows <https://www.conventionalcommits.org/en/v1.0.0/> to track changes.
4040
### Added
4141

4242
- Add a helper macro `#[dynify]` for trait transformations ({{PRNUM}}).
43+
- Support downcasting a `Buffered` pointer (#10).
44+
45+
[#10]: https://github.com/loichyan/dynify/pull/10
46+
[{{PRNUM}}]: https://github.com/loichyan/dynify/pull/{{PRNUM}}
4347

4448
## [0.1.0] - 2025-07-06
4549

src/constructor.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ impl<T: ?Sized> Opaque<T> {
209209
pub trait Dynify: Construct {
210210
/// Constructs the object in the supplied container.
211211
///
212-
/// For non-panicking variant, use [`try_init`](Self::try_init).
212+
/// For a non-panicking alternative see [`try_init`](Self::try_init).
213213
///
214214
/// # Panic
215215
///
@@ -245,7 +245,7 @@ pub trait Dynify: Construct {
245245

246246
/// Constructs the object in two containers in turn.
247247
///
248-
/// For non-panicking variant, use [`try_init2`](Self::try_init2).
248+
/// For a non-panicking alternative see [`try_init2`](Self::try_init2).
249249
///
250250
/// # Examples
251251
///
@@ -302,7 +302,7 @@ impl<T: Construct> Dynify for T {}
302302
pub trait PinDynify: PinConstruct {
303303
/// Constructs the object in the supplied container.
304304
///
305-
/// For non-panicking variant, use [`try_pin_init`](Self::try_pin_init).
305+
/// For a non-panicking alternative see [`try_pin_init`](Self::try_pin_init).
306306
///
307307
/// # Panic
308308
///
@@ -338,7 +338,7 @@ pub trait PinDynify: PinConstruct {
338338

339339
/// Constructs the object in two containers in turn.
340340
///
341-
/// For non-panicking variant, use [`try_pin_init2`](Self::try_pin_init2).
341+
/// For a non-panicking alternative see [`try_pin_init2`](Self::try_pin_init2).
342342
///
343343
/// # Panic
344344
///

src/container.rs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use core::alloc::Layout;
2+
use core::any::Any;
23
use core::fmt;
34
use core::marker::PhantomData;
45
use core::mem::MaybeUninit;
@@ -143,6 +144,69 @@ impl<'a, T: ?Sized> Buffered<'a, T> {
143144
}
144145
}
145146
}
147+
impl<'a> Buffered<'a, dyn Any> {
148+
/// Attempts to downcast the pointer to a concrete type.
149+
pub fn downcast<T: Any>(self) -> Result<Buffered<'a, T>, Self> {
150+
if self.is::<T>() {
151+
unsafe { Ok(self.downcast_unchecked()) }
152+
} else {
153+
Err(self)
154+
}
155+
}
156+
157+
/// Downcasts the box to a concrete type.
158+
///
159+
/// For a safe alternative see [`downcast`](Self::downcast).
160+
///
161+
/// # Safety
162+
///
163+
/// The contained value must be of type `T`.
164+
pub unsafe fn downcast_unchecked<T: Any>(self) -> Buffered<'a, T> {
165+
Buffered::from_raw(self.into_raw().cast())
166+
}
167+
}
168+
impl<'a> Buffered<'a, dyn Any + Send> {
169+
/// Attempts to downcast the pointer to a concrete type.
170+
pub fn downcast<T: Any>(self) -> Result<Buffered<'a, T>, Self> {
171+
if self.is::<T>() {
172+
unsafe { Ok(self.downcast_unchecked()) }
173+
} else {
174+
Err(self)
175+
}
176+
}
177+
178+
/// Downcasts the box to a concrete type.
179+
///
180+
/// For a safe alternative see [`downcast`](Self::downcast).
181+
///
182+
/// # Safety
183+
///
184+
/// The contained value must be of type `T`.
185+
pub unsafe fn downcast_unchecked<T: Any>(self) -> Buffered<'a, T> {
186+
Buffered::from_raw(self.into_raw().cast())
187+
}
188+
}
189+
impl<'a> Buffered<'a, dyn Any + Send + Sync> {
190+
/// Attempts to downcast the pointer to a concrete type.
191+
pub fn downcast<T: Any>(self) -> Result<Buffered<'a, T>, Self> {
192+
if self.is::<T>() {
193+
unsafe { Ok(self.downcast_unchecked()) }
194+
} else {
195+
Err(self)
196+
}
197+
}
198+
199+
/// Downcasts the box to a concrete type.
200+
///
201+
/// For a safe alternative see [`downcast`](Self::downcast).
202+
///
203+
/// # Safety
204+
///
205+
/// The contained value must be of type `T`.
206+
pub unsafe fn downcast_unchecked<T: Any>(self) -> Buffered<'a, T> {
207+
Buffered::from_raw(self.into_raw().cast())
208+
}
209+
}
146210

147211
// Pretend `Buffered` owns the value of `T` rather than just a pointer to it.
148212
// This, along with the `Buffered::project*` APIs, makes it easy to obtain a

src/container_tests.rs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use smallvec::SmallVec;
88

99
use super::*;
1010
use crate::utils::*;
11-
use crate::{from_closure, Dynify};
11+
use crate::{from_closure, Dynify, Opaque};
1212

1313
trait DebugEmplace: Emplace<dyn Any, Err = Self::__Err> {
1414
type __Err: std::fmt::Debug;
@@ -177,3 +177,29 @@ fn clean_up_boxed_zst_on_panic() {
177177
fn clean_up_boxed_on_panic() {
178178
let _ = from_closure::<usize, usize, _>(|_| panic!("just panic")).boxed();
179179
}
180+
181+
#[rstest]
182+
#[case(fastrand::i32(..))]
183+
#[case(randstr(16..32))]
184+
fn downcast_buffered<T>(#[case] data: T)
185+
where
186+
T: Any + Clone + core::fmt::Debug + Eq + Send + Sync,
187+
{
188+
struct Impossbile;
189+
let mut stack = newstk::<32>();
190+
191+
let init = from_closure(|slot| slot.write(data.clone()) as &mut Opaque<dyn Any>);
192+
let val = stack.emplace(init).unwrap();
193+
let val = val.downcast::<Impossbile>().err().unwrap();
194+
assert_eq!(val.downcast().ok().as_deref(), Some(&data));
195+
196+
let init = from_closure(|slot| slot.write(data.clone()) as &mut Opaque<dyn Any + Send>);
197+
let val = stack.emplace(init).unwrap();
198+
let val = val.downcast::<Impossbile>().err().unwrap();
199+
assert_eq!(val.downcast().ok().as_deref(), Some(&data));
200+
201+
let init = from_closure(|slot| slot.write(data.clone()) as &mut Opaque<dyn Any + Send + Sync>);
202+
let val = stack.emplace(init).unwrap();
203+
let val = val.downcast::<Impossbile>().err().unwrap();
204+
assert_eq!(val.downcast().ok().as_deref(), Some(&data));
205+
}

src/lib.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,3 +137,5 @@ have these concerns, it's better to go with the battle tested async-trait.
137137
- **smallvec**: Enable container implementations for `SmallVec`, a drop-in
138138
replacement for `[u8; N] + Vec<u8>`.
139139
- **macros**: Enable helpful procedural macros.
140+
141+
[`SmallVec`]: smallvec::SmallVec

0 commit comments

Comments
 (0)