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

#![feature(arc_new_cyclic)]

//! Functions like Arc::new_cyclic which work for a varied number of arguments
pub mod arc {
    use std::sync::{Arc, Weak};

    pub fn new_cyclic_2<A, B>(data_fn: impl FnOnce(&Weak<A>, &Weak<B>) -> (A, B)) -> (Arc<A>, Arc<B>) {
        let mut b: Option<Arc<B>> = None;

        let a = Arc::new_cyclic(|weak_a| {
            let mut a : Option<A> = None;
            b = Some(Arc::new_cyclic(|weak_b| {
                let (a2, b2) = data_fn(weak_a, weak_b);
                a = Some(a2);
                b2
            }));
            a.unwrap()
        });

        (a, b.unwrap())
    }

    pub fn new_cyclic_3<A, B, C>(data_fn: impl FnOnce(&Weak<A>, &Weak<B>, &Weak<C>) -> (A, B, C)) -> (Arc<A>, Arc<B>, Arc<C>) {
        let mut c: Option<Arc<C>> = None;
        let mut b: Option<Arc<B>> = None;

        let a = Arc::new_cyclic(|weak_a| {
            let mut a : Option<A> = None;
            b = Some(Arc::new_cyclic(|weak_b| {
                let mut b: Option<B> = None;
                c = Some(Arc::new_cyclic(|weak_c| {
                    let (a2, b2, c2) = data_fn(weak_a, weak_b, weak_c);
                    a = Some(a2);
                    b = Some(b2);
                    c2
                }));
                b.unwrap()
            }));
            a.unwrap()
        });

        (a, b.unwrap(), c.unwrap())
    }
}

#[cfg(test)]
mod tests {
    use super::arc;

    #[test]
    fn arc_new_cyclic_2_immediate_upgrades_fail() {
        arc::new_cyclic_2(|weak_a, weak_b| {
            assert_eq!(weak_a.upgrade(), None);
            assert_eq!(weak_b.upgrade(), None);

            (1, 2)
        });
    }

    #[test]
    fn arc_new_cyclic_2_immediate_dereferences_pass() {
        let (a, b) = arc::new_cyclic_2(|weak_a, weak_b| {
            assert_eq!(weak_a.upgrade(), None);
            assert_eq!(weak_b.upgrade(), None);

            (1, 2)
        });

        assert_eq!(*a, 1);
        assert_eq!(*b, 2);
    }

    #[test]
    fn arc_new_cyclic_2_deferred_upgrades_fail() {
        let mut a = None;
        let mut b = None;

        let stored = arc::new_cyclic_2(|weak_a, weak_b| {
            a = Some(weak_a.clone());
            b = Some(weak_b.clone());

            (1, 2)
        });

        assert_eq!(*a.expect("Failed to store").upgrade().expect("Upgrade failed"), 1);
        assert_eq!(*b.expect("Failed to store").upgrade().expect("Upgrade failed"), 2);
        drop(stored);
    }

    #[test]
    fn arc_new_cyclic_3_immediate_upgrades_fail() {
        arc::new_cyclic_3(|weak_a, weak_b, weak_c| {
            assert_eq!(weak_a.upgrade(), None);
            assert_eq!(weak_b.upgrade(), None);
            assert_eq!(weak_c.upgrade(), None);

            (1, 2, 3)
        });
    }

    #[test]
    fn arc_new_cyclic_3_immediate_dereferences_pass() {
        let (a, b, c) = arc::new_cyclic_3(|weak_a, weak_b, weak_c| {
            assert_eq!(weak_a.upgrade(), None);
            assert_eq!(weak_b.upgrade(), None);
            assert_eq!(weak_c.upgrade(), None);

            (1, 2, 3)
        });

        assert_eq!(*a, 1);
        assert_eq!(*b, 2);
        assert_eq!(*c, 3);
    }

    #[test]
    fn arc_new_cyclic_3_deferred_upgrades() {
        let mut a = None;
        let mut b = None;
        let mut c = None;

        let stored = arc::new_cyclic_3(|weak_a, weak_b, weak_c| {
            a = Some(weak_a.clone());
            b = Some(weak_b.clone());
            c = Some(weak_c.clone());

            (1, 2, 3)
        });

        assert_eq!(*a.expect("Failed to store").upgrade().expect("Upgrade failed"), 1);
        assert_eq!(*b.expect("Failed to store").upgrade().expect("Upgrade failed"), 2);
        assert_eq!(*c.expect("Failed to store").upgrade().expect("Upgrade failed"), 3);
        drop(stored);
    }
}