Crate scopeguard
source ·Expand description
A scope guard will run a given closure when it goes out of scope, even if the code between panics. (as long as panic doesn’t abort)
Examples
Hello World
This example creates a scope guard with an example function:
extern crate scopeguard;
fn f() {
let _guard = scopeguard::guard((), |_| {
println!("Hello Scope Exit!");
});
// rest of the code here.
// Here, at the end of `_guard`'s scope, the guard's closure is called.
// It is also called if we exit this scope through unwinding instead.
}
defer!
Use the defer
macro to run an operation at scope exit,
either regular scope exit or during unwinding from a panic.
#[macro_use(defer)] extern crate scopeguard;
use std::cell::Cell;
fn main() {
// use a cell to observe drops during and after the scope guard is active
let drop_counter = Cell::new(0);
{
// Create a scope guard using `defer!` for the current scope
defer! {
drop_counter.set(1 + drop_counter.get());
}
// Do regular operations here in the meantime.
// Just before scope exit: it hasn't run yet.
assert_eq!(drop_counter.get(), 0);
// The following scope end is where the defer closure is called
}
assert_eq!(drop_counter.get(), 1);
}
Scope Guard with Value
If the scope guard closure needs to access an outer value that is also mutated outside of the scope guard, then you may want to use the scope guard with a value. The guard works like a smart pointer, so the inner value can be accessed by reference or by mutable reference.
1. The guard owns a file
In this example, the scope guard owns a file and ensures pending writes are synced at scope exit.
extern crate scopeguard;
use std::fs::*;
use std::io::{self, Write};
fn try_main() -> io::Result<()> {
let f = File::create("newfile.txt")?;
let mut file = scopeguard::guard(f, |f| {
// ensure we flush file at return or panic
let _ = f.sync_all();
});
// Access the file through the scope guard itself
file.write_all(b"test me\n").map(|_| ())
}
fn main() {
try_main().unwrap();
}
2. The guard restores an invariant on scope exit
extern crate scopeguard;
use std::mem::ManuallyDrop;
use std::ptr;
// This function, just for this example, takes the first element
// and inserts it into the assumed sorted tail of the vector.
//
// For optimization purposes we temporarily violate an invariant of the
// Vec, that it owns all of its elements.
//
// The safe approach is to use swap, which means two writes to memory,
// the optimization is to use a “hole” which uses only one write of memory
// for each position it moves.
//
// We *must* use a scope guard to run this code safely. We
// are running arbitrary user code (comparison operators) that may panic.
// The scope guard ensures we restore the invariant after successful
// exit or during unwinding from panic.
fn insertion_sort_first<T>(v: &mut Vec<T>)
where T: PartialOrd
{
struct Hole<'a, T: 'a> {
v: &'a mut Vec<T>,
index: usize,
value: ManuallyDrop<T>,
}
unsafe {
// Create a moved-from location in the vector, a “hole”.
let value = ptr::read(&v[0]);
let mut hole = Hole { v: v, index: 0, value: ManuallyDrop::new(value) };
// Use a scope guard with a value.
// At scope exit, plug the hole so that the vector is fully
// initialized again.
// The scope guard owns the hole, but we can access it through the guard.
let mut hole_guard = scopeguard::guard(hole, |hole| {
// plug the hole in the vector with the value that was // taken out
let index = hole.index;
ptr::copy_nonoverlapping(&*hole.value, &mut hole.v[index], 1);
});
// run algorithm that moves the hole in the vector here
// move the hole until it's in a sorted position
for i in 1..hole_guard.v.len() {
if *hole_guard.value >= hole_guard.v[i] {
// move the element back and the hole forward
let index = hole_guard.index;
hole_guard.v.swap(index, index + 1);
hole_guard.index += 1;
} else {
break;
}
}
// When the scope exits here, the Vec becomes whole again!
}
}
fn main() {
let string = String::from;
let mut data = vec![string("c"), string("a"), string("b"), string("d")];
insertion_sort_first(&mut data);
assert_eq!(data, vec!["a", "b", "c", "d"]);
}
Crate Features
use_std
- Enabled by default. Enables the
OnUnwind
andOnSuccess
strategies. - Disable to use
no_std
.
- Enabled by default. Enables the
Rust Version
This version of the crate requires Rust 1.20 or later.
The scopeguard 1.x release series will use a carefully considered version upgrade policy, where in a later 1.x version, we will raise the minimum required Rust version.
Macros
- Macro to create a
ScopeGuard
(always run).
Structs
ScopeGuard
is a scope guard that may own a protected value.
Enums
- Always run on scope exit.
Traits
- Controls in which cases the associated code should be run
Functions
- Create a new
ScopeGuard
owningv
and with deferred closuredropfn
.