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