1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
//! Hand-rolled implementations of pervasive `StackBox<dyn …>` types.
//!
//! ### Explanation
//!
//! Given the baked-into-the-language nature of trait objects, receiver types,
//! and auto-generated vtables, things like `StackBox<'_, dyn Any>` _& co._
//! Do Not Work™.
//!
//! So we need to hand-roll those, and this is what this module is about.
//!
//! ___
//!
//! Currently, only basic `FnOnce()` signatures and `Any` are supported,
//! since they are the only traits in the standard library with (part of) their
//! API _consuming ownership_ of the trait object, leading to `&'_ mut dyn …`
//! not sufficing (_c.f._, the classic "wrap that `FnOnce()` into an `Option`
//! to get an `FnMut()` you can thus type-erase with `&mut dyn FnMut()`"; which
//! exposes the pattern to runtime panics if the consumer of the
//! `&mut dyn FnMut()` calls it multiple times).
//!
//! ## Creation
//!
//! The `StackBoxDyn…` family of types is created using the
//! [`.into_dyn()`][`StackBox::into_dyn`] method. Such method is based on **an
//! internal trait**, that defines the vtable and usability of these wrapper
//! types, but the next section shows how downstream users may add
//! implementations of this trait.
//!
//! ### Custom-defined `StackBoxDyn…`s
//!
//! These can now be created thanks to the powerful [`custom_dyn!`] macro.
//!
//! [`custom_dyn!`]: `crate::custom_dyn`

pub
mod any;

mod custom_dyn;

pub
mod fn_once;

use crate::{
    marker::{NoAutoTraits, Sendness, Syncness},
    prelude::*,
};
use ::core::ptr;

mod ty { pub struct Erased(()); }

pub(in crate)
mod __ {
    pub
    trait DynCoerce<StackBoxImplTrait> {
        fn fatten (it: StackBoxImplTrait)
          -> Self /* StackBoxDynTrait */
        ;
    }
}
use __::DynCoerce;

impl<'frame, ImplTrait : 'frame> StackBox<'frame, ImplTrait> {
    /// Coerces a `StackBox<impl Trait>` into a `StackBox<dyn Trait>`, provided
    /// the `Trait` is [one of the supported ones][`self`].
    pub
    fn into_dyn<StackBoxDynTrait> (self: StackBox<'frame, ImplTrait>)
      -> StackBoxDynTrait
    where
        StackBoxDynTrait : DynCoerce<StackBox<'frame, ImplTrait>>,
    {
        DynCoerce::fatten(self)
    }
}

#[cfg(any(test, doctest))]
mod tests;

#[cfg(test)]
mod my_test {
    use ::stackbox::prelude::*;

    /// Hack to have invocations work inside function bodies for the MSRV.
    macro_rules! custom_dyn {
        (
            @as_item
            $item:item
        ) => (
            $item
        );

        (
            $($input:tt)*
        ) => (
            custom_dyn! {@as_item
                ::stackbox::prelude::custom_dyn! {
                    $($input)*
                }
            }
        );
    }

    #[test]
    fn fn_once_higher_order_param ()
    {
        custom_dyn! {
            dyn FnOnceRef<Arg> : FnOnce(&Arg)
            where {
                Arg : ?Sized,
            }
            {
                fn call (
                    self: Self,
                    s: &'_ Arg,
                )
                {
                    self(s)
                }
            }
        }

        let not_send = 42;
        stackbox!(let f = |_: &str| drop(not_send));
        let f: StackBoxDynFnOnceRef<'_, str, dyn Send + Sync> = f.into_dyn();
        let f = |s: &str| f.call(s);
        f("");
    }

    #[test]
    fn manual_any_non_owned_receiver ()
    {
        use ::core::any;
        custom_dyn! {
            dyn Any<'__> : any::Any
            {
                fn type_id (self: &'_ Self, __: &'__ ())
                  -> any::TypeId
                {
                    any::TypeId::of::<Self>()
                }
            }
        }

        let it = ();
        stackbox!(let it);
        let it: StackBoxDynAny<'_, '_> = it.into_dyn();
        assert_eq!(it.type_id(&()), any::TypeId::of::<()>());
        assert_eq!(it.type_id(&()), any::TypeId::of::<()>());
    }
}