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