lazynodes.rs 2.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465
  1. //! Support for storing lazy-nodes on the stack
  2. //!
  3. //! This module provides support for a type called `LazyNodes` which is a micro-heap located on the stack to make calls
  4. //! to `rsx!` more efficient.
  5. //!
  6. //! To support returning rsx! from branches in match statements, we need to use dynamic dispatch on [`ScopeState`] closures.
  7. //!
  8. //! This can be done either through boxing directly, or by using dynamic-sized-types and a custom allocator. In our case,
  9. //! we build a tiny alloactor in the stack and allocate the closure into that.
  10. //!
  11. //! The logic for this was borrowed from <https://docs.rs/stack_dst/0.6.1/stack_dst/>. Unfortunately, this crate does not
  12. //! support non-static closures, so we've implemented the core logic of `ValueA` in this module.
  13. use smallbox::{smallbox, space::S16, SmallBox};
  14. use crate::{innerlude::VNode, ScopeState};
  15. /// A concrete type provider for closures that build [`VNode`] structures.
  16. ///
  17. /// This struct wraps lazy structs that build [`VNode`] trees Normally, we cannot perform a blanket implementation over
  18. /// closures, but if we wrap the closure in a concrete type, we can use it for different branches in matching.
  19. ///
  20. ///
  21. /// ```rust, ignore
  22. /// LazyNodes::new(|f| f.element("div", [], [], [] None))
  23. /// ```
  24. pub struct LazyNodes<'a, 'b> {
  25. inner: Box<dyn FnMut(&'a ScopeState) -> VNode<'a> + 'b>,
  26. // inner: SmallBox<dyn FnMut(&'a ScopeState) -> VNode<'a> + 'b, S16>,
  27. }
  28. impl<'a, 'b> LazyNodes<'a, 'b> {
  29. /// Create a new [`LazyNodes`] closure, optimistically placing it onto the stack.
  30. ///
  31. /// If the closure cannot fit into the stack allocation (16 bytes), then it
  32. /// is placed on the heap. Most closures will fit into the stack, and is
  33. /// the most optimal way to use the creation function.
  34. pub fn new(val: impl FnOnce(&'a ScopeState) -> VNode<'a> + 'b) -> Self {
  35. // there's no way to call FnOnce without a box, so we need to store it in a slot and use static dispatch
  36. let mut slot = Some(val);
  37. Self {
  38. inner: Box::new(move |f| {
  39. let val = slot.take().expect("cannot call LazyNodes twice");
  40. val(f)
  41. }),
  42. // inner: smallbox!(move |f| {
  43. // let val = slot.take().expect("cannot call LazyNodes twice");
  44. // val(f)
  45. // }),
  46. }
  47. }
  48. /// Call the closure with the given factory to produce real [`VNode`].
  49. ///
  50. /// ```rust, ignore
  51. /// let f = LazyNodes::new(move |f| f.element("div", [], [], [] None));
  52. ///
  53. /// let node = f.call(cac);
  54. /// ```
  55. #[must_use]
  56. pub fn call(mut self, f: &'a ScopeState) -> VNode<'a> {
  57. (self.inner)(f)
  58. }
  59. }