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