]> git.proxmox.com Git - rustc.git/blame - src/doc/nomicon/destructors.md
Imported Upstream version 1.6.0+dfsg1
[rustc.git] / src / doc / nomicon / destructors.md
CommitLineData
c1a9b12d
SL
1% Destructors
2
3What the language *does* provide is full-blown automatic destructors through the
4`Drop` trait, which provides the following method:
5
6```rust,ignore
7fn drop(&mut self);
8```
9
10This 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
13of `self`.**
14
15This is a convenience feature so that you don't have to write "destructor
16boilerplate" to drop children. If a struct has no special logic for being
17dropped other than dropping its children, then it means `Drop` doesn't need to
18be implemented at all!
19
b039eaaf 20**There is no stable way to prevent this behavior in Rust 1.0.**
c1a9b12d
SL
21
22Note that taking `&mut self` means that even if you could suppress recursive
23Drop, Rust will prevent you from e.g. moving fields out of self. For most types,
24this is totally fine.
25
26For instance, a custom implementation of `Box` might write `Drop` like this:
27
28```rust
92a42be0 29#![feature(alloc, heap_api, drop_in_place, unique)]
e9174d1e
SL
30
31extern crate alloc;
c1a9b12d 32
92a42be0 33use std::ptr::{drop_in_place, Unique};
c1a9b12d
SL
34use std::mem;
35
e9174d1e
SL
36use alloc::heap;
37
c1a9b12d
SL
38struct Box<T>{ ptr: Unique<T> }
39
40impl<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}
e9174d1e 50# fn main() {}
c1a9b12d
SL
51```
52
53and this works fine because when Rust goes to drop the `ptr` field it just sees
e9174d1e 54a [Unique] that has no actual `Drop` implementation. Similarly nothing can
b039eaaf 55use-after-free the `ptr` because when drop exits, it becomes inaccessible.
c1a9b12d
SL
56
57However this wouldn't work:
58
59```rust
92a42be0 60#![feature(alloc, heap_api, drop_in_place, unique)]
e9174d1e
SL
61
62extern crate alloc;
c1a9b12d 63
92a42be0 64use std::ptr::{drop_in_place, Unique};
c1a9b12d
SL
65use std::mem;
66
e9174d1e
SL
67use alloc::heap;
68
c1a9b12d
SL
69struct Box<T>{ ptr: Unique<T> }
70
71impl<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
82struct SuperBox<T> { my_box: Box<T> }
83
84impl<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}
e9174d1e 95# fn main() {}
c1a9b12d
SL
96```
97
98After we deallocate the `box`'s ptr in SuperBox's destructor, Rust will
99happily proceed to tell the box to Drop itself and everything will blow up with
100use-after-frees and double-frees.
101
b039eaaf 102Note that the recursive drop behavior applies to all structs and enums
c1a9b12d
SL
103regardless of whether they implement Drop. Therefore something like
104
105```rust
106struct Boxy<T> {
107 data1: Box<T>,
108 data2: Box<T>,
109 info: u32,
110}
111```
112
113will have its data1 and data2's fields destructors whenever it "would" be
114dropped, 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
117Similarly,
118
119```rust
120enum Link {
121 Next(Box<Link>),
122 None,
123}
124```
125
126will have its inner Box field dropped if and only if an instance stores the
127Next variant.
128
e9174d1e 129In general this works really nicely because you don't need to worry about
c1a9b12d
SL
130adding/removing drops when you refactor your data layout. Still there's
131certainly many valid usecases for needing to do trickier things with
132destructors.
133
134The classic safe solution to overriding recursive drop and allowing moving out
135of Self during `drop` is to use an Option:
136
137```rust
92a42be0 138#![feature(alloc, heap_api, drop_in_place, unique)]
e9174d1e
SL
139
140extern crate alloc;
c1a9b12d 141
92a42be0 142use std::ptr::{drop_in_place, Unique};
c1a9b12d
SL
143use std::mem;
144
e9174d1e
SL
145use alloc::heap;
146
c1a9b12d
SL
147struct Box<T>{ ptr: Unique<T> }
148
149impl<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
160struct SuperBox<T> { my_box: Option<Box<T>> }
161
162impl<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}
e9174d1e 176# fn main() {}
c1a9b12d
SL
177```
178
179However this has fairly odd semantics: you're saying that a field that *should*
180always be Some *may* be None, just because that happens in the destructor. Of
181course this conversely makes a lot of sense: you can call arbitrary methods on
182self during the destructor, and this should prevent you from ever doing so after
183deinitializing the field. Not that it will prevent you from producing any other
184arbitrarily invalid state in there.
185
186On balance this is an ok choice. Certainly what you should reach for by default.
187However, in the future we expect there to be a first-class way to announce that
188a field shouldn't be automatically dropped.
189
190[Unique]: phantom-data.html