]> git.lizzy.rs Git - rust.git/blob - src/doc/tarpl/destructors.md
34c8b2b8624d3122cc4a4d52fbe1311a089bff99
[rust.git] / src / doc / tarpl / destructors.md
1 % Destructors
2
3 What the language *does* provide is full-blown automatic destructors through the
4 `Drop` trait, which provides the following method:
5
6 ```rust,ignore
7 fn drop(&mut self);
8 ```
9
10 This method gives the type time to somehow finish what it was doing. **After
11 `drop` is run, Rust will recursively try to drop all of the fields of `self`**.
12 This is a convenience feature so that you don't have to write "destructor
13 boilerplate" to drop children. If a struct has no special logic for being
14 dropped other than dropping its children, then it means `Drop` doesn't need to
15 be implemented at all!
16
17 **There is no stable way to prevent this behaviour in Rust 1.0.
18
19 Note that taking `&mut self` means that even if you *could* suppress recursive
20 Drop, Rust will prevent you from e.g. moving fields out of self. For most types,
21 this is totally fine.
22
23 For instance, a custom implementation of `Box` might write `Drop` like this:
24
25 ```rust
26 #![feature(heap_api, core_intrinsics, unique)]
27
28 use std::rt::heap;
29 use std::ptr::Unique;
30 use std::intrinsics::drop_in_place;
31 use std::mem;
32
33 struct Box<T>{ ptr: Unique<T> }
34
35 impl<T> Drop for Box<T> {
36     fn drop(&mut self) {
37         unsafe {
38             drop_in_place(*self.ptr);
39             heap::deallocate((*self.ptr) as *mut u8,
40                              mem::size_of::<T>(),
41                              mem::align_of::<T>());
42         }
43     }
44 }
45 ```
46
47 and this works fine because when Rust goes to drop the `ptr` field it just sees
48 a [Unique][] that has no actual `Drop` implementation. Similarly nothing can
49 use-after-free the `ptr` because when drop exits, it becomes inacessible.
50
51 However this wouldn't work:
52
53 ```rust
54 #![feature(heap_api, core_intrinsics, unique)]
55
56 use std::rt::heap;
57 use std::ptr::Unique;
58 use std::intrinsics::drop_in_place;
59 use std::mem;
60
61 struct Box<T>{ ptr: Unique<T> }
62
63 impl<T> Drop for Box<T> {
64     fn drop(&mut self) {
65         unsafe {
66             drop_in_place(*self.ptr);
67             heap::deallocate((*self.ptr) as *mut u8,
68                              mem::size_of::<T>(),
69                              mem::align_of::<T>());
70         }
71     }
72 }
73
74 struct SuperBox<T> { my_box: Box<T> }
75
76 impl<T> Drop for SuperBox<T> {
77     fn drop(&mut self) {
78         unsafe {
79             // Hyper-optimized: deallocate the box's contents for it
80             // without `drop`ing the contents
81             heap::deallocate((*self.my_box.ptr) as *mut u8,
82                              mem::size_of::<T>(),
83                              mem::align_of::<T>());
84         }
85     }
86 }
87 ```
88
89 After we deallocate the `box`'s ptr in SuperBox's destructor, Rust will
90 happily proceed to tell the box to Drop itself and everything will blow up with
91 use-after-frees and double-frees.
92
93 Note that the recursive drop behaviour applies to *all* structs and enums
94 regardless of whether they implement Drop. Therefore something like
95
96 ```rust
97 struct Boxy<T> {
98     data1: Box<T>,
99     data2: Box<T>,
100     info: u32,
101 }
102 ```
103
104 will have its data1 and data2's fields destructors whenever it "would" be
105 dropped, even though it itself doesn't implement Drop. We say that such a type
106 *needs Drop*, even though it is not itself Drop.
107
108 Similarly,
109
110 ```rust
111 enum Link {
112     Next(Box<Link>),
113     None,
114 }
115 ```
116
117 will have its inner Box field dropped *if and only if* an instance stores the
118 Next variant.
119
120 In general this works really nice because you don't need to worry about
121 adding/removing drops when you refactor your data layout. Still there's
122 certainly many valid usecases for needing to do trickier things with
123 destructors.
124
125 The classic safe solution to overriding recursive drop and allowing moving out
126 of Self during `drop` is to use an Option:
127
128 ```rust
129 #![feature(heap_api, core_intrinsics, unique)]
130
131 use std::rt::heap;
132 use std::ptr::Unique;
133 use std::intrinsics::drop_in_place;
134 use std::mem;
135
136 struct Box<T>{ ptr: Unique<T> }
137
138 impl<T> Drop for Box<T> {
139     fn drop(&mut self) {
140         unsafe {
141             drop_in_place(*self.ptr);
142             heap::deallocate((*self.ptr) as *mut u8,
143                              mem::size_of::<T>(),
144                              mem::align_of::<T>());
145         }
146     }
147 }
148
149 struct SuperBox<T> { my_box: Option<Box<T>> }
150
151 impl<T> Drop for SuperBox<T> {
152     fn drop(&mut self) {
153         unsafe {
154             // Hyper-optimized: deallocate the box's contents for it
155             // without `drop`ing the contents. Need to set the `box`
156             // field as `None` to prevent Rust from trying to Drop it.
157             let my_box = self.my_box.take().unwrap();
158             heap::deallocate((*my_box.ptr) as *mut u8,
159                              mem::size_of::<T>(),
160                              mem::align_of::<T>());
161             mem::forget(my_box);
162         }
163     }
164 }
165 ```
166
167 However this has fairly odd semantics: you're saying that a field that *should*
168 always be Some may be None, just because that happens in the destructor. Of
169 course this conversely makes a lot of sense: you can call arbitrary methods on
170 self during the destructor, and this should prevent you from ever doing so after
171 deinitializing the field. Not that it will prevent you from producing any other
172 arbitrarily invalid state in there.
173
174 On balance this is an ok choice. Certainly what you should reach for by default.
175 However, in the future we expect there to be a first-class way to announce that
176 a field shouldn't be automatically dropped.
177
178 [Unique]: phantom-data.html