lazy_init.rs 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. //! `LazyStatic` like initialization.
  2. /// The initialization state
  3. enum State<F, T> {
  4. /// The data is uninitialized, initialization is pending.
  5. ///
  6. /// The inner closure contains the initialization function.
  7. Uninitialized(F),
  8. /// The data is initialized, and ready for use.
  9. Initialized(T),
  10. /// The data is unreachable, panick!
  11. Unreachable,
  12. }
  13. /// A lazily initialized container.
  14. ///
  15. /// This container starts out simply containing an initializer (i.e., a function to construct the
  16. /// value in question). When the value is requested, the initializer runs.
  17. pub struct LazyInit<F, T> {
  18. /// The internal state.
  19. state: State<F, T>,
  20. }
  21. impl<F: FnMut() -> T, T> LazyInit<F, T> {
  22. /// Create a new to-be-initialized container.
  23. ///
  24. /// The closure will be executed when initialization is required, and is guaranteed to be
  25. /// executed at most once.
  26. #[inline]
  27. pub const fn new(init: F) -> LazyInit<F, T> {
  28. LazyInit {
  29. state: State::Uninitialized(init),
  30. }
  31. }
  32. /// Create a lazy initializer with unreachable inner data.
  33. ///
  34. /// When access is tried, it will simply issue a panic. This is useful for e.g. temporarily
  35. /// getting ownership.
  36. pub const fn unreachable() -> LazyInit<F, T> {
  37. LazyInit {
  38. state: State::Unreachable,
  39. }
  40. }
  41. /// Get a mutable reference to the inner value.
  42. ///
  43. /// If it is uninitialize, it will be initialized and then returned.
  44. #[inline]
  45. pub fn get(&mut self) -> &mut T {
  46. let inner;
  47. match self.state {
  48. State::Initialized(ref mut x) => return x,
  49. State::Uninitialized(ref mut f) => inner = f(),
  50. State::Unreachable => unreachable!(),
  51. }
  52. self.state = State::Initialized(inner);
  53. if let State::Initialized(ref mut x) = self.state {
  54. x
  55. } else {
  56. // TODO find a better way.
  57. unreachable!();
  58. }
  59. }
  60. /// Get the inner of the container.
  61. ///
  62. /// This won't mutate the container itself, since it consumes it. The initializer will (if
  63. /// necessary) be called and the result returned. If already initialized, the inner value will
  64. /// be moved out and returned.
  65. pub fn into_inner(self) -> T {
  66. match self.state {
  67. State::Initialized(x) => x,
  68. State::Uninitialized(mut f) => f(),
  69. State::Unreachable => unreachable!(),
  70. }
  71. }
  72. }
  73. #[cfg(test)]
  74. mod test {
  75. use super::*;
  76. use core::cell::Cell;
  77. #[test]
  78. fn test_init() {
  79. let mut lazy = LazyInit::new(|| 300);
  80. assert_eq!(*lazy.get(), 300);
  81. *lazy.get() = 400;
  82. assert_eq!(*lazy.get(), 400);
  83. }
  84. #[test]
  85. fn test_laziness() {
  86. let is_called = Cell::new(false);
  87. let mut lazy = LazyInit::new(|| is_called.set(true));
  88. assert!(!is_called.get());
  89. lazy.get();
  90. assert!(is_called.get());
  91. }
  92. }