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
//! This module presents various kinds of pointers and their behaviour under [Clone].
//!
//! The example covered in tests demonstrates the comparison between
//!  1. Shared references that can be simply cloned (*shallow copied*)
//!  1. [Box] pointer to heap data that, because they are *owned*, delegate cloning to the owned
//!     instance (i.e. are potentially *deep copied*)
//!  1. [Rc] smart pointer which implements [Clone] by imcrementing reference counter and returning
//!     a cheap copy of itself with the same data reference (i.e. *shallow copy* at the cost of an
//!     additional counter)
use std::fmt::Debug;
use std::rc::Rc;

/// Thin wrapper around [usize] serving as an internal counter for the number of clones
#[derive(Debug, Default)]
pub struct Data(usize);

/// Custom clone implementation for [Data] in which new data have counter incremented by one
impl Clone for Data {
    fn clone(&self) -> Self {
        Self(self.0 + 1)
    }
}

/// Container for [Data] allocated and owned in various ways.
///
/// This class can derive [Clone] because [Data] are [Clone] and so are [Box], [Rc] and shared
/// references `&'a`.
#[derive(Clone, Debug)]
pub struct Container<'a> {
    /// Owned data located on the *stack*
    pub owned: Data,
    /// Immutably shared data located on the *stack* that must outlive container's lifetime `'a`
    pub stack_shared: &'a Data,
    /// Pointer to owned data located on the *heap*
    pub heap_owned: Box<Data>,
    /// Reference counting pointer to shared data located on the *heap*
    pub heap_shared: Rc<Data>,
}

#[cfg(test)]
mod tests {

    use super::*;

    #[test]
    fn it_works() {
        // Allocate new data on the stack
        let stack_data = Data::default();

        // Allocate new reference-counted data on the heap
        let rc_data = Rc::new(Data::default());

        // Allocate two containers on the heap (behind a `Box`)

        let box1 = Box::new(Container {
            owned: Data::default(),
            stack_shared: &stack_data,
            heap_owned: Data::default().into(),
            heap_shared: rc_data.clone(),
        });

        let box2 = Box::new(Container {
            owned: Data::default(),
            stack_shared: &stack_data,
            heap_owned: Data::default().into(),
            heap_shared: rc_data.clone(),
        });

        // Clone both containers
        let clone1 = box1.clone();
        let clone2 = box2.clone();

        // Owned data are cloned because the container is behind a `Box`
        assert_eq!(clone1.owned.0, 1);
        assert_eq!(clone2.owned.0, 1);

        // Data behind `&` are *not* cloned, only the pointer is trivially copied
        assert_eq!(stack_data.0, 0);
        assert_eq!(clone1.stack_shared.0, 0);
        assert_eq!(clone2.stack_shared.0, 0);

        // Heap allocated data (behind a `Box`) are cloned (also for the same reason as above)
        assert_eq!(clone1.heap_owned.0, 1);
        assert_eq!(clone2.heap_owned.0, 1);

        // Data behind `Rc` are *not* cloned, only the pointer is (and the counter is incremented)
        assert_eq!(rc_data.0, 0);
        assert_eq!(clone1.heap_shared.0, 0);
        assert_eq!(clone2.heap_shared.0, 0);

        // Heap data behind `Rc` are still valid after a shared reference is dropped
        drop(box1);
        assert_eq!(rc_data.0, 0);
        assert_eq!(clone2.heap_shared.0, 0);
    }
}