Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

re-do documentation for Drop #57449

Closed
wants to merge 2 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 87 additions & 42 deletions src/libcore/ops/drop.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
/// Used to run some code when a value goes out of scope.
/// This is sometimes called a 'destructor'.
/// Destructors for cleaning up resources
///
/// When a value goes out of scope, it will have its `drop` method called if
/// its type implements `Drop`. Then, any fields the value contains will also
/// be dropped recursively.
/// When a value is no longer needed, Rust will run a "destructor" on that value.
/// The most common way that a value is no longer needed is when it goes out of
/// scope. Destructors may still run in other circumstances, but we're going to
/// focus on scope for the examples here. To learn about some of those other cases,
/// please see [the reference] section on destructors.
///
/// Because of this recursive dropping, you do not need to implement this trait
/// unless your type needs its own destructor logic.
/// [the reference]: https://doc.rust-lang.org/reference/destructors.html
///
/// Refer to [the chapter on `Drop` in *The Rust Programming Language*][book]
/// for some more elaboration.
///
/// [book]: ../../book/ch15-03-drop.html
///
/// # Examples
/// The `Drop` trait provides a way to implement a custom destructor on a type.
/// Why would you want such a thing? Well, many types aren't only data: they
/// manage some sort of resource. That resource may be memory, it may be a file
/// descriptor, it may be a network socket. But when the type is no longer going
/// to be used, it should "clean up" that resource by freeing the memory or
/// closing the file or socket. This is the job of a destructor, and therefore
/// the job for `Drop::drop`.
///
/// ## Implementing `Drop`
///
/// The `drop` method is called when `_x` goes out of scope, and therefore
/// `main` prints `Dropping!`.
/// When a value goes out of scope, it will call `Drop::drop` on that value. For example,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it will call

What is "it"?

///
/// ```
/// ```rust
/// struct HasDrop;
///
/// impl Drop for HasDrop {
Expand All @@ -34,52 +34,97 @@
/// }
/// ```
///
/// ## Dropping is done recursively
/// Here, `main` will print `Dropping!`. In other words, the compiler generates code that
/// kind of looks like this:
///
/// ```rust,compile_fail,E0040
/// # struct HasDrop;
///
/// When `outer` goes out of scope, the `drop` method will be called first for
/// `Outer`, then for `Inner`. Therefore, `main` prints `Dropping Outer!` and
/// then `Dropping Inner!`.
/// # impl Drop for HasDrop {
/// # fn drop(&mut self) {
/// # println!("Dropping!");
/// # }
/// # }
/// fn main() {
/// let _x = HasDrop;
///
/// let mut x = _x;
/// Drop::drop(&mut x);
/// }
/// ```
/// struct Inner;
/// struct Outer(Inner);
///
/// impl Drop for Inner {
/// Our custom implementation of `Drop` will be called at the end of `main`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A little misleading here, it seems as if there's a default implementation of Drop, which is not true.

///
/// ## You cannot call `Drop::drop` yourself
///
/// Because the compiler automatically calls `Drop::drop`, you cannot call it yourself. This
/// would lead to "double drop", where `Drop::drop` is called twice on the same value. This
/// can lead to things like "double frees".
///
/// In other words, if you tried to write the code in the previous example with the explicit
/// call to `Drop::drop`, you'd get a compiler error.
///
/// If you'd like to call `drop` yourself, there is something you can do: call [`std::mem::drop`].
/// This function will drop its argument. For more, see its documentation.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For more, see its documentation.

Is this required when the function itself is linked?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm trying to allude to there being more to it, if that makes any sense.

///
/// [`std::mem::drop`]: ../../std/mem/fn.drop.html
///
/// ## `Drop` is recursive
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it's the destructor that is recursive...

///
/// If your type is something like a `struct` or `enum` that is an aggregate of other types, then
/// Rust will call `Drop::drop` on the type first, and then recursively on everything it contains.
/// For example:
///
/// ```rust
/// struct HasDrop;
///
/// impl Drop for HasDrop {
/// fn drop(&mut self) {
/// println!("Dropping Inner!");
/// println!("Dropping HasDrop!");
/// }
/// }
///
/// impl Drop for Outer {
/// struct HasTwoDrops {
/// one: HasDrop,
/// two: HasDrop,
/// }
///
/// impl Drop for HasTwoDrops {
/// fn drop(&mut self) {
/// println!("Dropping Outer!");
/// println!("Dropping HasTwoDrops!");
/// }
/// }
///
/// fn main() {
/// let _x = Outer(Inner);
/// let _x = HasTwoDrops { one: HasDrop, two: HasDrop };
/// }
/// ```
///
/// ## Variables are dropped in reverse order of declaration
///
/// `_first` is declared first and `_second` is declared second, so `main` will
/// print `Declared second!` and then `Declared first!`.
/// This will print
///
/// ```text
/// Dropping HasTwoDrops!
/// Dropping HasDrop!
/// Dropping HasDrop!
/// ```
/// struct PrintOnDrop(&'static str);
///
/// impl Drop for PrintOnDrop {
/// fn drop(&mut self) {
/// println!("{}", self.0);
/// }
/// }
/// Which of our two `HasDrop` drops first, though? For structs, it's the same
/// order that they're declared: first `one`, then `two`. If you'd like to try
/// this yourself, you can modify `HasDrop` above to contain some data, like an
/// integer, and then use it in the `println!` inside of `Drop`. This behavior is
/// guaranteed by the language. In other situations, the rules may be different,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Basicly there're two drop orders in Rust: by declare order (for structs/arrays), by reverse order (for stack variables). Only talk one of them, but omit another, will mislead people (to think that variables are droped by declare order too).

/// but they are always well-defined. Please see [the reference] for the full
/// rules.
///
/// fn main() {
/// let _first = PrintOnDrop("Declared first!");
/// let _second = PrintOnDrop("Declared second!");
/// }
/// ```
/// [the reference]: https://doc.rust-lang.org/reference/destructors.html
///
/// ## `Copy` and `Drop` are exclusive
///
/// You cannot implement both [`Copy`] and `Drop` on the same type. Types that
/// are `Copy` don't manage resources, and can be freely copied. As such, they
/// cannot have destructors.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rust terminology of "nontrivial destructors"

///
/// [`Copy`]: ../../std/marker/trait.Copy.html
#[lang = "drop"]
#[stable(feature = "rust1", since = "1.0.0")]
pub trait Drop {
Expand Down