| 1 | #![cfg_attr (not(any(test, feature = "use_std" )), no_std)] |
| 2 | #![doc (html_root_url = "https://docs.rs/scopeguard/1/" )] |
| 3 | |
| 4 | //! A scope guard will run a given closure when it goes out of scope, |
| 5 | //! even if the code between panics. |
| 6 | //! (as long as panic doesn't abort) |
| 7 | //! |
| 8 | //! # Examples |
| 9 | //! |
| 10 | //! ## Hello World |
| 11 | //! |
| 12 | //! This example creates a scope guard with an example function: |
| 13 | //! |
| 14 | //! ``` |
| 15 | //! extern crate scopeguard; |
| 16 | //! |
| 17 | //! fn f() { |
| 18 | //! let _guard = scopeguard::guard((), |_| { |
| 19 | //! println!("Hello Scope Exit!" ); |
| 20 | //! }); |
| 21 | //! |
| 22 | //! // rest of the code here. |
| 23 | //! |
| 24 | //! // Here, at the end of `_guard`'s scope, the guard's closure is called. |
| 25 | //! // It is also called if we exit this scope through unwinding instead. |
| 26 | //! } |
| 27 | //! # fn main() { |
| 28 | //! # f(); |
| 29 | //! # } |
| 30 | //! ``` |
| 31 | //! |
| 32 | //! ## `defer!` |
| 33 | //! |
| 34 | //! Use the `defer` macro to run an operation at scope exit, |
| 35 | //! either regular scope exit or during unwinding from a panic. |
| 36 | //! |
| 37 | //! ``` |
| 38 | //! #[macro_use(defer)] extern crate scopeguard; |
| 39 | //! |
| 40 | //! use std::cell::Cell; |
| 41 | //! |
| 42 | //! fn main() { |
| 43 | //! // use a cell to observe drops during and after the scope guard is active |
| 44 | //! let drop_counter = Cell::new(0); |
| 45 | //! { |
| 46 | //! // Create a scope guard using `defer!` for the current scope |
| 47 | //! defer! { |
| 48 | //! drop_counter.set(1 + drop_counter.get()); |
| 49 | //! } |
| 50 | //! |
| 51 | //! // Do regular operations here in the meantime. |
| 52 | //! |
| 53 | //! // Just before scope exit: it hasn't run yet. |
| 54 | //! assert_eq!(drop_counter.get(), 0); |
| 55 | //! |
| 56 | //! // The following scope end is where the defer closure is called |
| 57 | //! } |
| 58 | //! assert_eq!(drop_counter.get(), 1); |
| 59 | //! } |
| 60 | //! ``` |
| 61 | //! |
| 62 | //! ## Scope Guard with Value |
| 63 | //! |
| 64 | //! If the scope guard closure needs to access an outer value that is also |
| 65 | //! mutated outside of the scope guard, then you may want to use the scope guard |
| 66 | //! with a value. The guard works like a smart pointer, so the inner value can |
| 67 | //! be accessed by reference or by mutable reference. |
| 68 | //! |
| 69 | //! ### 1. The guard owns a file |
| 70 | //! |
| 71 | //! In this example, the scope guard owns a file and ensures pending writes are |
| 72 | //! synced at scope exit. |
| 73 | //! |
| 74 | //! ``` |
| 75 | //! extern crate scopeguard; |
| 76 | //! |
| 77 | //! use std::fs::*; |
| 78 | //! use std::io::{self, Write}; |
| 79 | //! # // Mock file so that we don't actually write a file |
| 80 | //! # struct MockFile; |
| 81 | //! # impl MockFile { |
| 82 | //! # fn create(_s: &str) -> io::Result<Self> { Ok(MockFile) } |
| 83 | //! # fn write_all(&self, _b: &[u8]) -> io::Result<()> { Ok(()) } |
| 84 | //! # fn sync_all(&self) -> io::Result<()> { Ok(()) } |
| 85 | //! # } |
| 86 | //! # use self::MockFile as File; |
| 87 | //! |
| 88 | //! fn try_main() -> io::Result<()> { |
| 89 | //! let f = File::create("newfile.txt" )?; |
| 90 | //! let mut file = scopeguard::guard(f, |f| { |
| 91 | //! // ensure we flush file at return or panic |
| 92 | //! let _ = f.sync_all(); |
| 93 | //! }); |
| 94 | //! // Access the file through the scope guard itself |
| 95 | //! file.write_all(b"test me \n" ).map(|_| ()) |
| 96 | //! } |
| 97 | //! |
| 98 | //! fn main() { |
| 99 | //! try_main().unwrap(); |
| 100 | //! } |
| 101 | //! |
| 102 | //! ``` |
| 103 | //! |
| 104 | //! ### 2. The guard restores an invariant on scope exit |
| 105 | //! |
| 106 | //! ``` |
| 107 | //! extern crate scopeguard; |
| 108 | //! |
| 109 | //! use std::mem::ManuallyDrop; |
| 110 | //! use std::ptr; |
| 111 | //! |
| 112 | //! // This function, just for this example, takes the first element |
| 113 | //! // and inserts it into the assumed sorted tail of the vector. |
| 114 | //! // |
| 115 | //! // For optimization purposes we temporarily violate an invariant of the |
| 116 | //! // Vec, that it owns all of its elements. |
| 117 | //! // |
| 118 | //! // The safe approach is to use swap, which means two writes to memory, |
| 119 | //! // the optimization is to use a “hole” which uses only one write of memory |
| 120 | //! // for each position it moves. |
| 121 | //! // |
| 122 | //! // We *must* use a scope guard to run this code safely. We |
| 123 | //! // are running arbitrary user code (comparison operators) that may panic. |
| 124 | //! // The scope guard ensures we restore the invariant after successful |
| 125 | //! // exit or during unwinding from panic. |
| 126 | //! fn insertion_sort_first<T>(v: &mut Vec<T>) |
| 127 | //! where T: PartialOrd |
| 128 | //! { |
| 129 | //! struct Hole<'a, T: 'a> { |
| 130 | //! v: &'a mut Vec<T>, |
| 131 | //! index: usize, |
| 132 | //! value: ManuallyDrop<T>, |
| 133 | //! } |
| 134 | //! |
| 135 | //! unsafe { |
| 136 | //! // Create a moved-from location in the vector, a “hole”. |
| 137 | //! let value = ptr::read(&v[0]); |
| 138 | //! let mut hole = Hole { v: v, index: 0, value: ManuallyDrop::new(value) }; |
| 139 | //! |
| 140 | //! // Use a scope guard with a value. |
| 141 | //! // At scope exit, plug the hole so that the vector is fully |
| 142 | //! // initialized again. |
| 143 | //! // The scope guard owns the hole, but we can access it through the guard. |
| 144 | //! let mut hole_guard = scopeguard::guard(hole, |hole| { |
| 145 | //! // plug the hole in the vector with the value that was // taken out |
| 146 | //! let index = hole.index; |
| 147 | //! ptr::copy_nonoverlapping(&*hole.value, &mut hole.v[index], 1); |
| 148 | //! }); |
| 149 | //! |
| 150 | //! // run algorithm that moves the hole in the vector here |
| 151 | //! // move the hole until it's in a sorted position |
| 152 | //! for i in 1..hole_guard.v.len() { |
| 153 | //! if *hole_guard.value >= hole_guard.v[i] { |
| 154 | //! // move the element back and the hole forward |
| 155 | //! let index = hole_guard.index; |
| 156 | //! hole_guard.v.swap(index, index + 1); |
| 157 | //! hole_guard.index += 1; |
| 158 | //! } else { |
| 159 | //! break; |
| 160 | //! } |
| 161 | //! } |
| 162 | //! |
| 163 | //! // When the scope exits here, the Vec becomes whole again! |
| 164 | //! } |
| 165 | //! } |
| 166 | //! |
| 167 | //! fn main() { |
| 168 | //! let string = String::from; |
| 169 | //! let mut data = vec![string("c" ), string("a" ), string("b" ), string("d" )]; |
| 170 | //! insertion_sort_first(&mut data); |
| 171 | //! assert_eq!(data, vec!["a" , "b" , "c" , "d" ]); |
| 172 | //! } |
| 173 | //! |
| 174 | //! ``` |
| 175 | //! |
| 176 | //! |
| 177 | //! # Crate Features |
| 178 | //! |
| 179 | //! - `use_std` |
| 180 | //! + Enabled by default. Enables the `OnUnwind` and `OnSuccess` strategies. |
| 181 | //! + Disable to use `no_std`. |
| 182 | //! |
| 183 | //! # Rust Version |
| 184 | //! |
| 185 | //! This version of the crate requires Rust 1.20 or later. |
| 186 | //! |
| 187 | //! The scopeguard 1.x release series will use a carefully considered version |
| 188 | //! upgrade policy, where in a later 1.x version, we will raise the minimum |
| 189 | //! required Rust version. |
| 190 | |
| 191 | #[cfg (not(any(test, feature = "use_std" )))] |
| 192 | extern crate core as std; |
| 193 | |
| 194 | use std::fmt; |
| 195 | use std::marker::PhantomData; |
| 196 | use std::mem::ManuallyDrop; |
| 197 | use std::ops::{Deref, DerefMut}; |
| 198 | use std::ptr; |
| 199 | |
| 200 | /// Controls in which cases the associated code should be run |
| 201 | pub trait Strategy { |
| 202 | /// Return `true` if the guard’s associated code should run |
| 203 | /// (in the context where this method is called). |
| 204 | fn should_run() -> bool; |
| 205 | } |
| 206 | |
| 207 | /// Always run on scope exit. |
| 208 | /// |
| 209 | /// “Always” run: on regular exit from a scope or on unwinding from a panic. |
| 210 | /// Can not run on abort, process exit, and other catastrophic events where |
| 211 | /// destructors don’t run. |
| 212 | #[derive (Debug)] |
| 213 | pub enum Always {} |
| 214 | |
| 215 | /// Run on scope exit through unwinding. |
| 216 | /// |
| 217 | /// Requires crate feature `use_std`. |
| 218 | #[cfg (feature = "use_std" )] |
| 219 | #[derive (Debug)] |
| 220 | pub enum OnUnwind {} |
| 221 | |
| 222 | /// Run on regular scope exit, when not unwinding. |
| 223 | /// |
| 224 | /// Requires crate feature `use_std`. |
| 225 | #[cfg (feature = "use_std" )] |
| 226 | #[derive (Debug)] |
| 227 | pub enum OnSuccess {} |
| 228 | |
| 229 | impl Strategy for Always { |
| 230 | #[inline (always)] |
| 231 | fn should_run() -> bool { |
| 232 | true |
| 233 | } |
| 234 | } |
| 235 | |
| 236 | #[cfg (feature = "use_std" )] |
| 237 | impl Strategy for OnUnwind { |
| 238 | #[inline ] |
| 239 | fn should_run() -> bool { |
| 240 | std::thread::panicking() |
| 241 | } |
| 242 | } |
| 243 | |
| 244 | #[cfg (feature = "use_std" )] |
| 245 | impl Strategy for OnSuccess { |
| 246 | #[inline ] |
| 247 | fn should_run() -> bool { |
| 248 | !std::thread::panicking() |
| 249 | } |
| 250 | } |
| 251 | |
| 252 | /// Macro to create a `ScopeGuard` (always run). |
| 253 | /// |
| 254 | /// The macro takes statements, which are the body of a closure |
| 255 | /// that will run when the scope is exited. |
| 256 | #[macro_export ] |
| 257 | macro_rules! defer { |
| 258 | ($($t:tt)*) => { |
| 259 | let _guard = $crate::guard((), |()| { $($t)* }); |
| 260 | }; |
| 261 | } |
| 262 | |
| 263 | /// Macro to create a `ScopeGuard` (run on successful scope exit). |
| 264 | /// |
| 265 | /// The macro takes statements, which are the body of a closure |
| 266 | /// that will run when the scope is exited. |
| 267 | /// |
| 268 | /// Requires crate feature `use_std`. |
| 269 | #[cfg (feature = "use_std" )] |
| 270 | #[macro_export ] |
| 271 | macro_rules! defer_on_success { |
| 272 | ($($t:tt)*) => { |
| 273 | let _guard = $crate::guard_on_success((), |()| { $($t)* }); |
| 274 | }; |
| 275 | } |
| 276 | |
| 277 | /// Macro to create a `ScopeGuard` (run on unwinding from panic). |
| 278 | /// |
| 279 | /// The macro takes statements, which are the body of a closure |
| 280 | /// that will run when the scope is exited. |
| 281 | /// |
| 282 | /// Requires crate feature `use_std`. |
| 283 | #[cfg (feature = "use_std" )] |
| 284 | #[macro_export ] |
| 285 | macro_rules! defer_on_unwind { |
| 286 | ($($t:tt)*) => { |
| 287 | let _guard = $crate::guard_on_unwind((), |()| { $($t)* }); |
| 288 | }; |
| 289 | } |
| 290 | |
| 291 | /// `ScopeGuard` is a scope guard that may own a protected value. |
| 292 | /// |
| 293 | /// If you place a guard in a local variable, the closure can |
| 294 | /// run regardless how you leave the scope — through regular return or panic |
| 295 | /// (except if panic or other code aborts; so as long as destructors run). |
| 296 | /// It is run only once. |
| 297 | /// |
| 298 | /// The `S` parameter for [`Strategy`](trait.Strategy.html) determines if |
| 299 | /// the closure actually runs. |
| 300 | /// |
| 301 | /// The guard's closure will be called with the held value in the destructor. |
| 302 | /// |
| 303 | /// The `ScopeGuard` implements `Deref` so that you can access the inner value. |
| 304 | pub struct ScopeGuard<T, F, S = Always> |
| 305 | where |
| 306 | F: FnOnce(T), |
| 307 | S: Strategy, |
| 308 | { |
| 309 | value: ManuallyDrop<T>, |
| 310 | dropfn: ManuallyDrop<F>, |
| 311 | // fn(S) -> S is used, so that the S is not taken into account for auto traits. |
| 312 | strategy: PhantomData<fn(S) -> S>, |
| 313 | } |
| 314 | |
| 315 | impl<T, F, S> ScopeGuard<T, F, S> |
| 316 | where |
| 317 | F: FnOnce(T), |
| 318 | S: Strategy, |
| 319 | { |
| 320 | /// Create a `ScopeGuard` that owns `v` (accessible through deref) and calls |
| 321 | /// `dropfn` when its destructor runs. |
| 322 | /// |
| 323 | /// The `Strategy` decides whether the scope guard's closure should run. |
| 324 | #[inline ] |
| 325 | #[must_use ] |
| 326 | pub fn with_strategy(v: T, dropfn: F) -> ScopeGuard<T, F, S> { |
| 327 | ScopeGuard { |
| 328 | value: ManuallyDrop::new(v), |
| 329 | dropfn: ManuallyDrop::new(dropfn), |
| 330 | strategy: PhantomData, |
| 331 | } |
| 332 | } |
| 333 | |
| 334 | /// “Defuse” the guard and extract the value without calling the closure. |
| 335 | /// |
| 336 | /// ``` |
| 337 | /// extern crate scopeguard; |
| 338 | /// |
| 339 | /// use scopeguard::{guard, ScopeGuard}; |
| 340 | /// |
| 341 | /// fn conditional() -> bool { true } |
| 342 | /// |
| 343 | /// fn main() { |
| 344 | /// let mut guard = guard(Vec::new(), |mut v| v.clear()); |
| 345 | /// guard.push(1); |
| 346 | /// |
| 347 | /// if conditional() { |
| 348 | /// // a condition maybe makes us decide to |
| 349 | /// // “defuse” the guard and get back its inner parts |
| 350 | /// let value = ScopeGuard::into_inner(guard); |
| 351 | /// } else { |
| 352 | /// // guard still exists in this branch |
| 353 | /// } |
| 354 | /// } |
| 355 | /// ``` |
| 356 | #[inline ] |
| 357 | pub fn into_inner(guard: Self) -> T { |
| 358 | // Cannot move out of `Drop`-implementing types, |
| 359 | // so `ptr::read` the value and forget the guard. |
| 360 | let mut guard = ManuallyDrop::new(guard); |
| 361 | unsafe { |
| 362 | let value = ptr::read(&*guard.value); |
| 363 | // Drop the closure after `value` has been read, so that if the |
| 364 | // closure's `drop` function panics, unwinding still tries to drop |
| 365 | // `value`. |
| 366 | ManuallyDrop::drop(&mut guard.dropfn); |
| 367 | value |
| 368 | } |
| 369 | } |
| 370 | } |
| 371 | |
| 372 | /// Create a new `ScopeGuard` owning `v` and with deferred closure `dropfn`. |
| 373 | #[inline ] |
| 374 | #[must_use ] |
| 375 | pub fn guard<T, F>(v: T, dropfn: F) -> ScopeGuard<T, F, Always> |
| 376 | where |
| 377 | F: FnOnce(T), |
| 378 | { |
| 379 | ScopeGuard::with_strategy(v, dropfn) |
| 380 | } |
| 381 | |
| 382 | /// Create a new `ScopeGuard` owning `v` and with deferred closure `dropfn`. |
| 383 | /// |
| 384 | /// Requires crate feature `use_std`. |
| 385 | #[cfg (feature = "use_std" )] |
| 386 | #[inline ] |
| 387 | #[must_use ] |
| 388 | pub fn guard_on_success<T, F>(v: T, dropfn: F) -> ScopeGuard<T, F, OnSuccess> |
| 389 | where |
| 390 | F: FnOnce(T), |
| 391 | { |
| 392 | ScopeGuard::with_strategy(v, dropfn) |
| 393 | } |
| 394 | |
| 395 | /// Create a new `ScopeGuard` owning `v` and with deferred closure `dropfn`. |
| 396 | /// |
| 397 | /// Requires crate feature `use_std`. |
| 398 | /// |
| 399 | /// ## Examples |
| 400 | /// |
| 401 | /// For performance reasons, or to emulate “only run guard on unwind” in |
| 402 | /// no-std environments, we can also use the default guard and simply manually |
| 403 | /// defuse it at the end of scope like the following example. (The performance |
| 404 | /// reason would be if the [`OnUnwind`]'s call to [std::thread::panicking()] is |
| 405 | /// an issue.) |
| 406 | /// |
| 407 | /// ``` |
| 408 | /// extern crate scopeguard; |
| 409 | /// |
| 410 | /// use scopeguard::ScopeGuard; |
| 411 | /// # fn main() { |
| 412 | /// { |
| 413 | /// let guard = scopeguard::guard((), |_| {}); |
| 414 | /// |
| 415 | /// // rest of the code here |
| 416 | /// |
| 417 | /// // we reached the end of scope without unwinding - defuse it |
| 418 | /// ScopeGuard::into_inner(guard); |
| 419 | /// } |
| 420 | /// # } |
| 421 | /// ``` |
| 422 | #[cfg (feature = "use_std" )] |
| 423 | #[inline ] |
| 424 | #[must_use ] |
| 425 | pub fn guard_on_unwind<T, F>(v: T, dropfn: F) -> ScopeGuard<T, F, OnUnwind> |
| 426 | where |
| 427 | F: FnOnce(T), |
| 428 | { |
| 429 | ScopeGuard::with_strategy(v, dropfn) |
| 430 | } |
| 431 | |
| 432 | // ScopeGuard can be Sync even if F isn't because the closure is |
| 433 | // not accessible from references. |
| 434 | // The guard does not store any instance of S, so it is also irrelevant. |
| 435 | unsafe impl<T, F, S> Sync for ScopeGuard<T, F, S> |
| 436 | where |
| 437 | T: Sync, |
| 438 | F: FnOnce(T), |
| 439 | S: Strategy, |
| 440 | { |
| 441 | } |
| 442 | |
| 443 | impl<T, F, S> Deref for ScopeGuard<T, F, S> |
| 444 | where |
| 445 | F: FnOnce(T), |
| 446 | S: Strategy, |
| 447 | { |
| 448 | type Target = T; |
| 449 | |
| 450 | fn deref(&self) -> &T { |
| 451 | &*self.value |
| 452 | } |
| 453 | } |
| 454 | |
| 455 | impl<T, F, S> DerefMut for ScopeGuard<T, F, S> |
| 456 | where |
| 457 | F: FnOnce(T), |
| 458 | S: Strategy, |
| 459 | { |
| 460 | fn deref_mut(&mut self) -> &mut T { |
| 461 | &mut *self.value |
| 462 | } |
| 463 | } |
| 464 | |
| 465 | impl<T, F, S> Drop for ScopeGuard<T, F, S> |
| 466 | where |
| 467 | F: FnOnce(T), |
| 468 | S: Strategy, |
| 469 | { |
| 470 | fn drop(&mut self) { |
| 471 | // This is OK because the fields are `ManuallyDrop`s |
| 472 | // which will not be dropped by the compiler. |
| 473 | let (value: T, dropfn: F) = unsafe { (ptr::read(&*self.value), ptr::read(&*self.dropfn)) }; |
| 474 | if S::should_run() { |
| 475 | dropfn(value); |
| 476 | } |
| 477 | } |
| 478 | } |
| 479 | |
| 480 | impl<T, F, S> fmt::Debug for ScopeGuard<T, F, S> |
| 481 | where |
| 482 | T: fmt::Debug, |
| 483 | F: FnOnce(T), |
| 484 | S: Strategy, |
| 485 | { |
| 486 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| 487 | f&mut DebugStruct<'_, '_>.debug_struct(stringify!(ScopeGuard)) |
| 488 | .field(name:"value" , &*self.value) |
| 489 | .finish() |
| 490 | } |
| 491 | } |
| 492 | |
| 493 | #[cfg (test)] |
| 494 | mod tests { |
| 495 | use super::*; |
| 496 | use std::cell::Cell; |
| 497 | use std::panic::catch_unwind; |
| 498 | use std::panic::AssertUnwindSafe; |
| 499 | |
| 500 | #[test ] |
| 501 | fn test_defer() { |
| 502 | let drops = Cell::new(0); |
| 503 | defer!(drops.set(1000)); |
| 504 | assert_eq!(drops.get(), 0); |
| 505 | } |
| 506 | |
| 507 | #[cfg (feature = "use_std" )] |
| 508 | #[test ] |
| 509 | fn test_defer_success_1() { |
| 510 | let drops = Cell::new(0); |
| 511 | { |
| 512 | defer_on_success!(drops.set(1)); |
| 513 | assert_eq!(drops.get(), 0); |
| 514 | } |
| 515 | assert_eq!(drops.get(), 1); |
| 516 | } |
| 517 | |
| 518 | #[cfg (feature = "use_std" )] |
| 519 | #[test ] |
| 520 | fn test_defer_success_2() { |
| 521 | let drops = Cell::new(0); |
| 522 | let _ = catch_unwind(AssertUnwindSafe(|| { |
| 523 | defer_on_success!(drops.set(1)); |
| 524 | panic!("failure" ) |
| 525 | })); |
| 526 | assert_eq!(drops.get(), 0); |
| 527 | } |
| 528 | |
| 529 | #[cfg (feature = "use_std" )] |
| 530 | #[test ] |
| 531 | fn test_defer_unwind_1() { |
| 532 | let drops = Cell::new(0); |
| 533 | let _ = catch_unwind(AssertUnwindSafe(|| { |
| 534 | defer_on_unwind!(drops.set(1)); |
| 535 | assert_eq!(drops.get(), 0); |
| 536 | panic!("failure" ) |
| 537 | })); |
| 538 | assert_eq!(drops.get(), 1); |
| 539 | } |
| 540 | |
| 541 | #[cfg (feature = "use_std" )] |
| 542 | #[test ] |
| 543 | fn test_defer_unwind_2() { |
| 544 | let drops = Cell::new(0); |
| 545 | { |
| 546 | defer_on_unwind!(drops.set(1)); |
| 547 | } |
| 548 | assert_eq!(drops.get(), 0); |
| 549 | } |
| 550 | |
| 551 | #[test ] |
| 552 | fn test_only_dropped_by_closure_when_run() { |
| 553 | let value_drops = Cell::new(0); |
| 554 | let value = guard((), |()| value_drops.set(1 + value_drops.get())); |
| 555 | let closure_drops = Cell::new(0); |
| 556 | let guard = guard(value, |_| closure_drops.set(1 + closure_drops.get())); |
| 557 | assert_eq!(value_drops.get(), 0); |
| 558 | assert_eq!(closure_drops.get(), 0); |
| 559 | drop(guard); |
| 560 | assert_eq!(value_drops.get(), 1); |
| 561 | assert_eq!(closure_drops.get(), 1); |
| 562 | } |
| 563 | |
| 564 | #[cfg (feature = "use_std" )] |
| 565 | #[test ] |
| 566 | fn test_dropped_once_when_not_run() { |
| 567 | let value_drops = Cell::new(0); |
| 568 | let value = guard((), |()| value_drops.set(1 + value_drops.get())); |
| 569 | let captured_drops = Cell::new(0); |
| 570 | let captured = guard((), |()| captured_drops.set(1 + captured_drops.get())); |
| 571 | let closure_drops = Cell::new(0); |
| 572 | let guard = guard_on_unwind(value, |value| { |
| 573 | drop(value); |
| 574 | drop(captured); |
| 575 | closure_drops.set(1 + closure_drops.get()) |
| 576 | }); |
| 577 | assert_eq!(value_drops.get(), 0); |
| 578 | assert_eq!(captured_drops.get(), 0); |
| 579 | assert_eq!(closure_drops.get(), 0); |
| 580 | drop(guard); |
| 581 | assert_eq!(value_drops.get(), 1); |
| 582 | assert_eq!(captured_drops.get(), 1); |
| 583 | assert_eq!(closure_drops.get(), 0); |
| 584 | } |
| 585 | |
| 586 | #[test ] |
| 587 | fn test_into_inner() { |
| 588 | let dropped = Cell::new(false); |
| 589 | let value = guard(42, |_| dropped.set(true)); |
| 590 | let guard = guard(value, |_| dropped.set(true)); |
| 591 | let inner = ScopeGuard::into_inner(guard); |
| 592 | assert_eq!(dropped.get(), false); |
| 593 | assert_eq!(*inner, 42); |
| 594 | } |
| 595 | } |
| 596 | |