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
//! This crate provides a macro to create a static value that gets created when needed
//! and dropped as soon as its not needed anymore.
//! It requires the `lazy_static` macro to be imported.
//!
//! # Example
//!
//! ```rust
//! #[macro_use] extern crate lazy_static;
//! #[macro_use] extern crate weak_static;
//! 
//! struct Foo;
//!
//! impl Foo {
//!     fn new() -> Self {
//!         println!("new");
//!         Foo
//!     }
//! }
//!
//! impl Drop for Foo {
//!     fn drop(&mut self) {
//!         println!("drop");
//!     }
//! }
//! 
//! weak_static! {
//!     static FOO: Foo = Foo::new();
//! }
//!
//! fn main() {
//!     {
//!         let _foo1 = FOO();
//!         let _foo2 = FOO();
//!         let _foo3 = FOO();
//!     }
//!     
//!     {
//!         let _foo4 = FOO();
//!         let _foo5 = FOO();
//!         let _foo6 = FOO();
//!     }
//! }
//! ```
//!
//! Outputs:
//!
//! ```text
//! new
//! drop
//! new
//! drop
//! ```
//!
//! The `new` prints corresponds to the `FOO()` calls of `_foo1` and `_foo4`.
//! The `drop` prints correspond to the last FOO reference being dropped.
//!

#[macro_export]
macro_rules! weak_static {
    (static $ident:ident : $typ:ty = $init:expr; ) => (
        #[allow(non_snake_case)]
        fn $ident() -> ::std::sync::Arc<$typ> {
            #[warn(non_snake_case)]
            {
                lazy_static! {
                    static ref VALUE: ::std::sync::Mutex<::std::sync::Weak<$typ>> =
                        ::std::default::Default::default();
                }
                
                let mut value = VALUE.lock().unwrap();
                
                value.upgrade().unwrap_or_else(|| {
                    let new_value = ::std::sync::Arc::new($init);

                    *value = ::std::sync::Arc::downgrade(&new_value);
                    
                    new_value
                })
            }
        }
    )
}