You've already forked comprehensive-rust
mirror of
https://github.com/google/comprehensive-rust.git
synced 2025-08-08 16:26:35 +02:00
apply dprint fmt
This commit is contained in:
@ -441,6 +441,7 @@
|
|||||||
- [Drop Limitations](idiomatic/leveraging-the-type-system/raii/drop_limitations.md)
|
- [Drop Limitations](idiomatic/leveraging-the-type-system/raii/drop_limitations.md)
|
||||||
- [Drop Bomb](idiomatic/leveraging-the-type-system/raii/drop_bomb.md)
|
- [Drop Bomb](idiomatic/leveraging-the-type-system/raii/drop_bomb.md)
|
||||||
- [Scope Guards](idiomatic/leveraging-the-type-system/raii/scope_guards.md)
|
- [Scope Guards](idiomatic/leveraging-the-type-system/raii/scope_guards.md)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Final Words
|
# Final Words
|
||||||
|
@ -4,11 +4,11 @@ minutes: 30
|
|||||||
|
|
||||||
# RAII and `Drop` in Practice
|
# RAII and `Drop` in Practice
|
||||||
|
|
||||||
RAII (*Resource Acquisition Is Initialization*)
|
RAII (_Resource Acquisition Is Initialization_) means tying the lifetime of a
|
||||||
means tying the lifetime of a resource to the lifetime of a value.
|
resource to the lifetime of a value.
|
||||||
|
|
||||||
Rust applies RAII automatically for memory management.
|
Rust applies RAII automatically for memory management. The `Drop` trait lets you
|
||||||
The `Drop` trait lets you extend this pattern to anything else.
|
extend this pattern to anything else.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
@ -26,28 +26,31 @@ fn main() {
|
|||||||
<details>
|
<details>
|
||||||
|
|
||||||
- In the above example
|
- In the above example
|
||||||
[the `Mutex`](https://doc.rust-lang.org/std/sync/struct.Mutex.html)
|
[the `Mutex`](https://doc.rust-lang.org/std/sync/struct.Mutex.html) owns its
|
||||||
owns its data: you can’t access the value inside without first acquiring the lock.
|
data: you can’t access the value inside without first acquiring the lock.
|
||||||
|
|
||||||
`mux.lock()` returns a
|
`mux.lock()` returns a
|
||||||
[`MutexGuard`](https://doc.rust-lang.org/std/sync/struct.MutexGuard.html),
|
[`MutexGuard`](https://doc.rust-lang.org/std/sync/struct.MutexGuard.html),
|
||||||
which [dereferences](https://doc.rust-lang.org/std/ops/trait.DerefMut.html)
|
which [dereferences](https://doc.rust-lang.org/std/ops/trait.DerefMut.html) to
|
||||||
to the data and implements [`Drop`](https://doc.rust-lang.org/std/ops/trait.Drop.html).
|
the data and implements
|
||||||
|
[`Drop`](https://doc.rust-lang.org/std/ops/trait.Drop.html).
|
||||||
|
|
||||||
- You may recall from [the Memory Management chapter](../../memory-management/drop.md)
|
- You may recall from
|
||||||
that the [`Drop` trait](https://doc.rust-lang.org/std/ops/trait.Drop.html)
|
[the Memory Management chapter](../../memory-management/drop.md) that the
|
||||||
lets you define what should happen when a resource is dropped.
|
[`Drop` trait](https://doc.rust-lang.org/std/ops/trait.Drop.html) lets you
|
||||||
|
define what should happen when a resource is dropped.
|
||||||
|
|
||||||
- In [the Blocks and Scopes chapter](../../control-flow-basics/blocks-and-scopes.md),
|
- In
|
||||||
we saw the most common situation where a resource is dropped:
|
[the Blocks and Scopes chapter](../../control-flow-basics/blocks-and-scopes.md),
|
||||||
when the scope of its _owner_ ends at the boundary of a block (`{}`).
|
we saw the most common situation where a resource is dropped: when the scope
|
||||||
|
of its _owner_ ends at the boundary of a block (`{}`).
|
||||||
|
|
||||||
- The use of
|
- The use of
|
||||||
[`std::mem::drop(val)`](https://doc.rust-lang.org/std/mem/fn.drop.html)
|
[`std::mem::drop(val)`](https://doc.rust-lang.org/std/mem/fn.drop.html)
|
||||||
allows you to _move_ a value out of scope before the block ends.
|
allows you to _move_ a value out of scope before the block ends.
|
||||||
|
|
||||||
- There are also other scenarios where this can happen,
|
- There are also other scenarios where this can happen, such as when the value
|
||||||
such as when the value owning the resource is "shadowed" by another value:
|
owning the resource is "shadowed" by another value:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
let a = String::from("foo");
|
let a = String::from("foo");
|
||||||
@ -55,62 +58,59 @@ fn main() {
|
|||||||
// because we shadow its binding with a new value.
|
// because we shadow its binding with a new value.
|
||||||
```
|
```
|
||||||
|
|
||||||
- Recall also from [the Drop chapter](../../memory-management/drop.md)
|
- Recall also from [the Drop chapter](../../memory-management/drop.md) that
|
||||||
that for a composite type such as a `struct`, all its fields will be dropped
|
for a composite type such as a `struct`, all its fields will be dropped when
|
||||||
when the struct itself is dropped.
|
the struct itself is dropped. If a field implements the `Drop` trait, its
|
||||||
If a field implements the `Drop` trait, its `Drop::drop`
|
`Drop::drop` _trait_ method will also be invoked.
|
||||||
_trait_ method will also be invoked.
|
|
||||||
|
|
||||||
- In any scenario where the stack unwinds the value, it is guaranteed
|
- In any scenario where the stack unwinds the value, it is guaranteed that the
|
||||||
that the [`Drop::drop`](https://doc.rust-lang.org/std/ops/trait.Drop.html#tymethod.drop)
|
[`Drop::drop`](https://doc.rust-lang.org/std/ops/trait.Drop.html#tymethod.drop)
|
||||||
method of a value `a` will be called.
|
method of a value `a` will be called.
|
||||||
|
|
||||||
- This holds true for happy paths such as:
|
- This holds true for happy paths such as:
|
||||||
|
|
||||||
- Exiting a block or function scope.
|
- Exiting a block or function scope.
|
||||||
|
|
||||||
- Returning early with an explicit `return` statement,
|
- Returning early with an explicit `return` statement, or implicitly by
|
||||||
or implicitly by using
|
using [the Try operator (`?`)](../../error-handling/try.md) to
|
||||||
[the Try operator (`?`)](../../error-handling/try.md)
|
early-return `Option` or `Result` values.
|
||||||
to early-return `Option` or `Result` values.
|
|
||||||
|
|
||||||
- It also holds for unexpected scenarios where a `panic` is triggered, if:
|
- It also holds for unexpected scenarios where a `panic` is triggered, if:
|
||||||
|
|
||||||
- The stack unwinds on panic (which is the default),
|
- The stack unwinds on panic (which is the default), allowing for graceful
|
||||||
allowing for graceful cleanup of resources.
|
cleanup of resources.
|
||||||
|
|
||||||
This unwind behavior can be overridden to instead
|
This unwind behavior can be overridden to instead
|
||||||
[abort on panic](https://github.com/rust-lang/rust/blob/master/library/panic_abort/src/lib.rs).
|
[abort on panic](https://github.com/rust-lang/rust/blob/master/library/panic_abort/src/lib.rs).
|
||||||
|
|
||||||
- No panic occurs within any of the `drop` methods
|
- No panic occurs within any of the `drop` methods invoked before reaching
|
||||||
invoked before reaching the `drop` call of the object `a`.
|
the `drop` call of the object `a`.
|
||||||
|
|
||||||
- Note that
|
- Note that
|
||||||
[an explicit exit of the program](https://doc.rust-lang.org/std/process/fn.exit.html),
|
[an explicit exit of the program](https://doc.rust-lang.org/std/process/fn.exit.html),
|
||||||
as sometimes used in CLI tools, terminates the process immediately.
|
as sometimes used in CLI tools, terminates the process immediately. In other
|
||||||
In other words, the stack is not unwound in this case,
|
words, the stack is not unwound in this case, and the `drop` method will not
|
||||||
and the `drop` method will not be called.
|
be called.
|
||||||
|
|
||||||
- `Drop` is a great fit for use cases like `Mutex`.
|
- `Drop` is a great fit for use cases like `Mutex`.
|
||||||
|
|
||||||
When the guard goes out of scope, [`Drop::drop`](https://doc.rust-lang.org/std/ops/trait.Drop.html#tymethod.drop)
|
When the guard goes out of scope,
|
||||||
|
[`Drop::drop`](https://doc.rust-lang.org/std/ops/trait.Drop.html#tymethod.drop)
|
||||||
is called and unlocks the mutex automatically.
|
is called and unlocks the mutex automatically.
|
||||||
|
|
||||||
In contrast to C++ or Java, where you often have to unlock manually
|
In contrast to C++ or Java, where you often have to unlock manually or use a
|
||||||
or use a `lock/unlock` pattern, Rust ensures the
|
`lock/unlock` pattern, Rust ensures the lock _cannot_ be forgotten, thanks to
|
||||||
lock *cannot* be forgotten, thanks to the compiler.
|
the compiler.
|
||||||
|
|
||||||
- In other scenarios, the `Drop` trait shows its limitations.
|
- In other scenarios, the `Drop` trait shows its limitations. Next, we'll look
|
||||||
Next, we'll look at what those are and how we can
|
at what those are and how we can address them.
|
||||||
address them.
|
|
||||||
|
|
||||||
## More to explore
|
## More to explore
|
||||||
|
|
||||||
To learn more about building synchronization primitives,
|
To learn more about building synchronization primitives, consider reading
|
||||||
consider reading [*Rust Atomics and Locks* by Mara Bos](https://marabos.nl/atomics/).
|
[_Rust Atomics and Locks_ by Mara Bos](https://marabos.nl/atomics/).
|
||||||
|
|
||||||
The book demonstrates, among other topics, how `Drop`
|
|
||||||
and RAII work together in constructs like `Mutex`.
|
|
||||||
|
|
||||||
|
The book demonstrates, among other topics, how `Drop` and RAII work together in
|
||||||
|
constructs like `Mutex`.
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# Drop Bombs: Enforcing API Correctness
|
# Drop Bombs: Enforcing API Correctness
|
||||||
|
|
||||||
Use `Drop` to enforce invariants and detect incorrect API usage.
|
Use `Drop` to enforce invariants and detect incorrect API usage. A "drop bomb"
|
||||||
A "drop bomb" panics if not defused.
|
panics if not defused.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
struct Transaction {
|
struct Transaction {
|
||||||
@ -35,28 +35,28 @@ impl Drop for Transaction {
|
|||||||
|
|
||||||
<details>
|
<details>
|
||||||
|
|
||||||
- The example above uses the drop bomb pattern to enforce at runtime that a transaction
|
- The example above uses the drop bomb pattern to enforce at runtime that a
|
||||||
is never dropped in an unfinished state. This applies to cases such as a database
|
transaction is never dropped in an unfinished state. This applies to cases
|
||||||
transaction that remains active in an external system.
|
such as a database transaction that remains active in an external system.
|
||||||
|
|
||||||
In this example, the programmer must finalize the transaction explicitly,
|
In this example, the programmer must finalize the transaction explicitly,
|
||||||
either by committing it or rolling it back to undo any changes.
|
either by committing it or rolling it back to undo any changes.
|
||||||
|
|
||||||
- In the context of FFI, where cross-boundary references are involved, it is often necessary
|
- In the context of FFI, where cross-boundary references are involved, it is
|
||||||
to ensure that manually allocated memory from the guest language is cleaned up through
|
often necessary to ensure that manually allocated memory from the guest
|
||||||
an explicit call to a safe API function.
|
language is cleaned up through an explicit call to a safe API function.
|
||||||
|
|
||||||
- Similar to unsafe code, it is recommended that APIs with expectations like these
|
- Similar to unsafe code, it is recommended that APIs with expectations like
|
||||||
are clearly documented under a Panic section. This helps ensure that users of the API
|
these are clearly documented under a Panic section. This helps ensure that
|
||||||
are aware of the consequences of misuse.
|
users of the API are aware of the consequences of misuse.
|
||||||
|
|
||||||
Ideally, drop bombs should be used only in internal APIs to catch bugs early,
|
Ideally, drop bombs should be used only in internal APIs to catch bugs early,
|
||||||
without placing implicit runtime obligations on library users.
|
without placing implicit runtime obligations on library users.
|
||||||
|
|
||||||
- If there is a way to restore the system to a valid state using a fallback
|
- If there is a way to restore the system to a valid state using a fallback in
|
||||||
in the Drop implementation, it is advisable to restrict the use of drop bombs
|
the Drop implementation, it is advisable to restrict the use of drop bombs to
|
||||||
to Debug mode. In Release mode, the Drop implementation could fall back to
|
Debug mode. In Release mode, the Drop implementation could fall back to safe
|
||||||
safe cleanup logic while still logging the incident as an error.
|
cleanup logic while still logging the incident as an error.
|
||||||
|
|
||||||
- Advanced use cases might also rely on the following patterns:
|
- Advanced use cases might also rely on the following patterns:
|
||||||
|
|
||||||
@ -68,20 +68,22 @@ impl Drop for Transaction {
|
|||||||
A zero-cost wrapper that disables the automatic drop behavior of a value,
|
A zero-cost wrapper that disables the automatic drop behavior of a value,
|
||||||
making manual cleanup required and explicit.
|
making manual cleanup required and explicit.
|
||||||
|
|
||||||
- The [`drop_bomb` crate](https://docs.rs/drop_bomb/latest/drop_bomb/)
|
- The [`drop_bomb` crate](https://docs.rs/drop_bomb/latest/drop_bomb/) provides
|
||||||
provides a way to enforce that certain values are not dropped unless explicitly defused.
|
a way to enforce that certain values are not dropped unless explicitly
|
||||||
It can be added to an existing struct and exposes a `.defuse()` method to make dropping safe.
|
defused. It can be added to an existing struct and exposes a `.defuse()`
|
||||||
The crate also includes a `DebugDropBomb` variant for use in debug-only builds.
|
method to make dropping safe. The crate also includes a `DebugDropBomb`
|
||||||
|
variant for use in debug-only builds.
|
||||||
|
|
||||||
## More to explore
|
## More to explore
|
||||||
|
|
||||||
Rust does not currently support full linear types or typestate programming
|
Rust does not currently support full linear types or typestate programming in
|
||||||
in the core language. This means the compiler cannot guarantee that a resource
|
the core language. This means the compiler cannot guarantee that a resource was
|
||||||
was used exactly once or finalized before being dropped.
|
used exactly once or finalized before being dropped.
|
||||||
|
|
||||||
Drop bombs serve as a runtime mechanism to enforce such usage invariants manually.
|
Drop bombs serve as a runtime mechanism to enforce such usage invariants
|
||||||
This is typically done in a Drop implementation that panics if a required method,
|
manually. This is typically done in a Drop implementation that panics if a
|
||||||
such as `.commit()`, was not called before the value went out of scope.
|
required method, such as `.commit()`, was not called before the value went out
|
||||||
|
of scope.
|
||||||
|
|
||||||
There is an open RFC issue and discussion about linear types in Rust:
|
There is an open RFC issue and discussion about linear types in Rust:
|
||||||
<https://github.com/rust-lang/rfcs/issues/814>.
|
<https://github.com/rust-lang/rfcs/issues/814>.
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
# The limitations of `Drop`
|
# The limitations of `Drop`
|
||||||
|
|
||||||
While `Drop` works well for cases
|
While `Drop` works well for cases like synchronization primitives, its use
|
||||||
like synchronization primitives, its use becomes more
|
becomes more questionable when dealing with I/O or unsafe resources.
|
||||||
questionable when dealing with I/O or unsafe resources.
|
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
@ -19,17 +18,17 @@ fn write_log() -> io::Result<()> {
|
|||||||
|
|
||||||
<details>
|
<details>
|
||||||
|
|
||||||
- In the earlier example, our `File` resource owns a file handle
|
- In the earlier example, our `File` resource owns a file handle provided by the
|
||||||
provided by the operating system.
|
operating system.
|
||||||
|
|
||||||
[As stated in the documentation](https://doc.rust-lang.org/std/fs/struct.File.html):
|
[As stated in the documentation](https://doc.rust-lang.org/std/fs/struct.File.html):
|
||||||
|
|
||||||
> Files are automatically closed when they go out of scope.
|
> Files are automatically closed when they go out of scope. Errors detected on
|
||||||
> Errors detected on closing are ignored by the implementation of Drop.
|
> closing are ignored by the implementation of Drop.
|
||||||
|
|
||||||
- This highlights a key limitation of the `Drop` trait:
|
- This highlights a key limitation of the `Drop` trait: it cannot propagate
|
||||||
it cannot propagate errors to the caller. In other words,
|
errors to the caller. In other words, fallible cleanup logic cannot be handled
|
||||||
fallible cleanup logic cannot be handled by the code using the `File`.
|
by the code using the `File`.
|
||||||
|
|
||||||
This becomes clear when looking at the
|
This becomes clear when looking at the
|
||||||
[definition of the `Drop` trait](https://doc.rust-lang.org/std/ops/trait.Drop.html):
|
[definition of the `Drop` trait](https://doc.rust-lang.org/std/ops/trait.Drop.html):
|
||||||
@ -41,45 +40,45 @@ fn write_log() -> io::Result<()> {
|
|||||||
```
|
```
|
||||||
|
|
||||||
Since `drop` does not return a `Result`, any error that occurs during cleanup
|
Since `drop` does not return a `Result`, any error that occurs during cleanup
|
||||||
cannot be surfaced or recovered from. This is by design:
|
cannot be surfaced or recovered from. This is by design: `drop` is invoked
|
||||||
`drop` is invoked automatically when a value is popped off the stack during
|
automatically when a value is popped off the stack during unwinding, leaving
|
||||||
unwinding, leaving no opportunity for error handling.
|
no opportunity for error handling.
|
||||||
|
|
||||||
- One workaround is to panic inside `drop` when a failure occurs.
|
- One workaround is to panic inside `drop` when a failure occurs. However, this
|
||||||
However, this is risky—if a panic happens while the stack is already unwinding,
|
is risky—if a panic happens while the stack is already unwinding, the program
|
||||||
the program will abort immediately, and remaining resources will not be cleaned up.
|
will abort immediately, and remaining resources will not be cleaned up.
|
||||||
|
|
||||||
While panicking in `drop` can serve certain purposes (see
|
While panicking in `drop` can serve certain purposes (see
|
||||||
[the next chapter on "drop bombs"](./drop_bomb.md)), it should be used sparingly
|
[the next chapter on "drop bombs"](./drop_bomb.md)), it should be used
|
||||||
and with full awareness of the consequences.
|
sparingly and with full awareness of the consequences.
|
||||||
|
|
||||||
- Another drawback of `drop` is that its execution is implicit and non-deterministic
|
- Another drawback of `drop` is that its execution is implicit and
|
||||||
in terms of timing. You cannot control *when* a value is dropped. And in fact as
|
non-deterministic in terms of timing. You cannot control _when_ a value is
|
||||||
discussed in previous slide it might never even run at all, leaving the external
|
dropped. And in fact as discussed in previous slide it might never even run at
|
||||||
resource in an undefined state.
|
all, leaving the external resource in an undefined state.
|
||||||
|
|
||||||
This matters particularly for I/O: normally you might set a timeout on blocking
|
This matters particularly for I/O: normally you might set a timeout on
|
||||||
operations, but when I/O occurs in a `drop` implementation, you have no way to
|
blocking operations, but when I/O occurs in a `drop` implementation, you have
|
||||||
enforce such constraints.
|
no way to enforce such constraints.
|
||||||
|
|
||||||
Returning to the `File` example: if the file handle hangs during close (e.g.,
|
Returning to the `File` example: if the file handle hangs during close (e.g.,
|
||||||
due to OS-level buffering or locking), the drop operation could block indefinitely.
|
due to OS-level buffering or locking), the drop operation could block
|
||||||
Since the call to `drop` happens implicitly and outside your control,
|
indefinitely. Since the call to `drop` happens implicitly and outside your
|
||||||
there's no way to apply a timeout or fallback mechanism.
|
control, there's no way to apply a timeout or fallback mechanism.
|
||||||
|
|
||||||
- For smart pointers and synchronization primitives, none of these drawbacks matter,
|
- For smart pointers and synchronization primitives, none of these drawbacks
|
||||||
since the operations are nearly instant and a program panic does not cause undefined behavior.
|
matter, since the operations are nearly instant and a program panic does not
|
||||||
The poisoned state disappears along with the termination of the program.
|
cause undefined behavior. The poisoned state disappears along with the
|
||||||
|
termination of the program.
|
||||||
|
|
||||||
- For use cases such as I/O or FFI, it may be preferable to let the user
|
- For use cases such as I/O or FFI, it may be preferable to let the user clean
|
||||||
clean up resources explicitly using a close function.
|
up resources explicitly using a close function.
|
||||||
|
|
||||||
However, this approach cannot be enforced at the type level.
|
However, this approach cannot be enforced at the type level. If explicit
|
||||||
If explicit cleanup is part of your API contract, you might choose to
|
cleanup is part of your API contract, you might choose to panic in drop when
|
||||||
panic in drop when the resource has not been properly closed.
|
the resource has not been properly closed. This can help catch contract
|
||||||
This can help catch contract violations at runtime.
|
violations at runtime.
|
||||||
|
|
||||||
This is one situation where drop bombs are useful,
|
This is one situation where drop bombs are useful, which we will discuss next.
|
||||||
which we will discuss next.
|
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
@ -1,13 +1,18 @@
|
|||||||
# Scope Guards
|
# Scope Guards
|
||||||
|
|
||||||
A scope guard makes use of the `Drop` trait
|
A scope guard makes use of the `Drop` trait to run a given closure when it goes
|
||||||
to run a given closure when it goes out of scope.
|
out of scope.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use std::{io::Write, fs::{self, File}};
|
use scopeguard::{ScopeGuard, guard};
|
||||||
use scopeguard::{guard, ScopeGuard};
|
use std::{
|
||||||
|
fs::{self, File},
|
||||||
|
io::Write,
|
||||||
|
};
|
||||||
|
|
||||||
fn conditional_success() -> bool { true }
|
fn conditional_success() -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let path = "temp.txt";
|
let path = "temp.txt";
|
||||||
@ -35,12 +40,12 @@ fn main() {
|
|||||||
<details>
|
<details>
|
||||||
|
|
||||||
- This example demonstrates the use of
|
- This example demonstrates the use of
|
||||||
[the `scopeguard` crate](https://docs.rs/scopeguard/latest/scopeguard/),
|
[the `scopeguard` crate](https://docs.rs/scopeguard/latest/scopeguard/), which
|
||||||
which is commonly used in internal APIs to ensure that a closure runs
|
is commonly used in internal APIs to ensure that a closure runs when a scope
|
||||||
when a scope exits.
|
exits.
|
||||||
|
|
||||||
- If the cleanup logic in the example above were unconditional,
|
- If the cleanup logic in the example above were unconditional, the code could
|
||||||
the code could be simplified using
|
be simplified using
|
||||||
[scopeguard's `defer!` macro](https://docs.rs/scopeguard/latest/scopeguard/#defer):
|
[scopeguard's `defer!` macro](https://docs.rs/scopeguard/latest/scopeguard/#defer):
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
@ -51,8 +56,8 @@ fn main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- If desired, the "scope guard" pattern can be implemented manually,
|
- If desired, the "scope guard" pattern can be implemented manually, starting as
|
||||||
starting as follows:
|
follows:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
struct ScopeGuard<T, F: FnOnce()> {
|
struct ScopeGuard<T, F: FnOnce()> {
|
||||||
@ -98,33 +103,32 @@ fn main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- The `ScopeGuard` type in the `scopeguard` crate also includes
|
- The `ScopeGuard` type in the `scopeguard` crate also includes a `Debug`
|
||||||
a `Debug` implementation and a third parameter:
|
implementation and a third parameter: a
|
||||||
a [`Strategy`](https://docs.rs/scopeguard/latest/scopeguard/trait.Strategy.html)
|
[`Strategy`](https://docs.rs/scopeguard/latest/scopeguard/trait.Strategy.html)
|
||||||
that determines when the `drop_fn` should run.
|
that determines when the `drop_fn` should run.
|
||||||
|
|
||||||
- By default, the strategy runs the drop function unconditionally.
|
- By default, the strategy runs the drop function unconditionally. However,
|
||||||
However, the crate also provides built-in strategies to run the drop function
|
the crate also provides built-in strategies to run the drop function only
|
||||||
only during unwinding (due to a panic), or only on successful scope exit.
|
during unwinding (due to a panic), or only on successful scope exit.
|
||||||
|
|
||||||
You can also implement your own `Strategy` trait
|
You can also implement your own `Strategy` trait to define custom
|
||||||
to define custom conditions for when the cleanup should occur.
|
conditions for when the cleanup should occur.
|
||||||
|
|
||||||
- Remark also that the crates' `ScopeGuard` makes use of
|
- Remark also that the crates' `ScopeGuard` makes use of
|
||||||
[`ManuallyDrop`](https://doc.rust-lang.org/std/mem/struct.ManuallyDrop.html)
|
[`ManuallyDrop`](https://doc.rust-lang.org/std/mem/struct.ManuallyDrop.html)
|
||||||
instead of `Option` to avoid automatic or premature dropping
|
instead of `Option` to avoid automatic or premature dropping of values,
|
||||||
of values, giving precise manual control and preventing
|
giving precise manual control and preventing double-drops. This avoids the
|
||||||
double-drops. This avoids the runtime overhead and semantic ambiguity that comes with using Option.
|
runtime overhead and semantic ambiguity that comes with using Option.
|
||||||
|
|
||||||
- Recalling the transaction example from
|
- Recalling the transaction example from
|
||||||
[the drop bombs chapter](./drop_bomb.md),
|
[the drop bombs chapter](./drop_bomb.md), we can now combine both concepts:
|
||||||
we can now combine both concepts:
|
define a fallback that runs unless we explicitly abort early. In the success
|
||||||
define a fallback that runs unless we explicitly abort early.
|
path, we call `ScopeGuard::into_inner` to prevent the rollback, as the
|
||||||
In the success path, we call `ScopeGuard::into_inner`
|
transaction has already been committed.
|
||||||
to prevent the rollback, as the transaction has already been committed.
|
|
||||||
|
|
||||||
While we still cannot propagate errors from fallible operations inside the drop logic,
|
While we still cannot propagate errors from fallible operations inside the
|
||||||
this pattern at least allows us to orchestrate fallbacks explicitly
|
drop logic, this pattern at least allows us to orchestrate fallbacks
|
||||||
and with whatever guarantees or limits we require.
|
explicitly and with whatever guarantees or limits we require.
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
Reference in New Issue
Block a user