Skip to content

Commit 66e98f4

Browse files
committed
re-do documentation for Drop
Fixes #36073 Drop's docs were not great, and as mentioned in #36073, focused too much on scope. I've re-done them a bit, using the same examples, and making it clear that scope is not the only thing that causes destructors to run.
1 parent b8c8f0b commit 66e98f4

File tree

1 file changed

+85
-42
lines changed

1 file changed

+85
-42
lines changed

src/libcore/ops/drop.rs

+85-42
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,26 @@
1-
/// Used to run some code when a value goes out of scope.
2-
/// This is sometimes called a 'destructor'.
1+
/// Destructors for cleaning up resources
32
///
4-
/// When a value goes out of scope, it will have its `drop` method called if
5-
/// its type implements `Drop`. Then, any fields the value contains will also
6-
/// be dropped recursively.
3+
/// When a value is no longer needed, Rust will run a "destructor" on that value.
4+
/// The most common way that a value is no longer needed is when it goes out of
5+
/// scope. Destructors may still run in other circumstances, but we're going to
6+
/// focus on scope for the examples here. To learn about some of those other cases,
7+
/// please see [the reference] section on destructors.
78
///
8-
/// Because of this recursive dropping, you do not need to implement this trait
9-
/// unless your type needs its own destructor logic.
9+
/// [the reference]: https://doc.rust-lang.org/reference/destructors.html
1010
///
11-
/// Refer to [the chapter on `Drop` in *The Rust Programming Language*][book]
12-
/// for some more elaboration.
13-
///
14-
/// [book]: ../../book/ch15-03-drop.html
15-
///
16-
/// # Examples
11+
/// The `Drop` trait provides a way to implement a custom destructor on a type.
12+
/// Why would you want such a thing? Well, many types aren't only data: they
13+
/// manage some sort of resource. That resource may be memory, it may be a file
14+
/// descriptor, it may be a network socket. But when the type is no longer going
15+
/// to be used, it should "clean up" that resource by freeing the memory or
16+
/// closing the file or socket. This is the job of a destructor, and therefore
17+
/// the job for `Drop::drop`.
1718
///
1819
/// ## Implementing `Drop`
1920
///
20-
/// The `drop` method is called when `_x` goes out of scope, and therefore
21-
/// `main` prints `Dropping!`.
21+
/// When a value goes out of scope, it will call `Drop::drop` on that value. For example,
2222
///
23-
/// ```
23+
/// ```rust
2424
/// struct HasDrop;
2525
///
2626
/// impl Drop for HasDrop {
@@ -34,52 +34,95 @@
3434
/// }
3535
/// ```
3636
///
37-
/// ## Dropping is done recursively
37+
/// Here, `main` will print `Dropping!`. In other words, the compiler generates code that
38+
/// kind of looks like this:
39+
///
40+
/// ```rust,compile_fail,E0040
41+
/// # struct HasDrop;
3842
///
39-
/// When `outer` goes out of scope, the `drop` method will be called first for
40-
/// `Outer`, then for `Inner`. Therefore, `main` prints `Dropping Outer!` and
41-
/// then `Dropping Inner!`.
43+
/// # impl Drop for HasDrop {
44+
/// # fn drop(&mut self) {
45+
/// # println!("Dropping!");
46+
/// # }
47+
/// # }
48+
/// fn main() {
49+
/// let _x = HasDrop;
4250
///
51+
/// let mut x = _x;
52+
/// Drop::drop(&mut x);
53+
/// }
4354
/// ```
44-
/// struct Inner;
45-
/// struct Outer(Inner);
4655
///
47-
/// impl Drop for Inner {
56+
/// As you can see, our custom implementation of `Drop` will be called at the end of `main`.
57+
///
58+
/// ## You cannot call `Drop::drop` yourself
59+
///
60+
/// Because the compiler automatically calls `Drop::drop`, you cannot call it yourself. This
61+
/// would lead to "double drop", where `Drop::drop` is called twice on the same value. This
62+
/// can lead to things like "double frees".
63+
///
64+
/// In other words, if you tried to write the code in the previous example with the explicit
65+
/// call to `Drop::drop`, you'd get a compiler error.
66+
///
67+
/// If you'd like to call `drop` yourself, there is something you can do: call [`std::mem::drop`].
68+
/// This function will drop its argument. For more, see its documentation.
69+
///
70+
/// [`std::mem::drop`]: ../../std/mem/fn.drop.html
71+
///
72+
/// ## `Drop` is recursive
73+
///
74+
/// If your type is something like a `struct` or `enum` that is an aggregate of other types, then
75+
/// Rust will call `Drop::drop` on the type first, and then recursively on everything it contains.
76+
/// For example:
77+
///
78+
/// ```rust
79+
/// struct HasDrop;
80+
///
81+
/// impl Drop for HasDrop {
4882
/// fn drop(&mut self) {
49-
/// println!("Dropping Inner!");
83+
/// println!("Dropping HasDrop!");
5084
/// }
5185
/// }
5286
///
53-
/// impl Drop for Outer {
87+
/// struct HasTwoDrops {
88+
/// one: HasDrop,
89+
/// two: HasDrop,
90+
/// }
91+
///
92+
/// impl Drop for HasTwoDrops {
5493
/// fn drop(&mut self) {
55-
/// println!("Dropping Outer!");
94+
/// println!("Dropping HasTwoDrops!");
5695
/// }
5796
/// }
5897
///
5998
/// fn main() {
60-
/// let _x = Outer(Inner);
99+
/// let _x = HasTwoDrops { one: HasDrop, two: HasDrop };
61100
/// }
62101
/// ```
63102
///
64-
/// ## Variables are dropped in reverse order of declaration
65-
///
66-
/// `_first` is declared first and `_second` is declared second, so `main` will
67-
/// print `Declared second!` and then `Declared first!`.
103+
/// This will print
68104
///
105+
/// ```text
106+
/// Dropping HasTwoDrops!
107+
/// Dropping HasDrop!
108+
/// Dropping HasDrop!
69109
/// ```
70-
/// struct PrintOnDrop(&'static str);
71110
///
72-
/// impl Drop for PrintOnDrop {
73-
/// fn drop(&mut self) {
74-
/// println!("{}", self.0);
75-
/// }
76-
/// }
111+
/// Similarly, a slice will drop each element in order.
77112
///
78-
/// fn main() {
79-
/// let _first = PrintOnDrop("Declared first!");
80-
/// let _second = PrintOnDrop("Declared second!");
81-
/// }
82-
/// ```
113+
/// Which of our two `HasDrop` drops first, though? It's the same order that
114+
/// they're declared: first `one`, then `two`. If you'd like to try this
115+
/// yourself, you can modify `HasDrop` above to contain some data, like an
116+
/// integer, and then use it in the `println!` inside of `Drop`. This behavior
117+
/// is guaranteed by the language.
118+
///
119+
/// ## `Copy` and `Drop` are exclusive
120+
///
121+
/// You cannot implement both [`Copy`] and `Drop` on the same type. Types that
122+
/// are `Copy` don't manage resources, and can be freely copied. As such, they
123+
/// cannot have destructors.
124+
///
125+
/// [`Copy`]: ../../std/marker/trait.Copy.html
83126
#[lang = "drop"]
84127
#[stable(feature = "rust1", since = "1.0.0")]
85128
pub trait Drop {

0 commit comments

Comments
 (0)