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