take.rs 1.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071
  1. //! Functions for temporarily moving out of ownership.
  2. // TODO: https://github.com/rust-lang/rfcs/pull/1736
  3. use core::{mem, intrinsics};
  4. /// A guarding type which will exit upon drop.
  5. ///
  6. /// This is used for catching unwinding and transforming it into abort.
  7. struct ExitGuard;
  8. impl Drop for ExitGuard {
  9. fn drop(&mut self) {
  10. // To make sure the user gets a meaningful error message (as opposed to a simple abort), we
  11. // log to the `ERROR` level.
  12. log!(ERROR, "Unwinding in a `take` closure.");
  13. // Just abort the program.
  14. unsafe { intrinsics::abort(); }
  15. }
  16. }
  17. /// Temporarily take ownership of the inner value of a reference.
  18. ///
  19. /// This essentially works as a generalized version of `mem::replace`, which instead takes a
  20. /// closure that will return the replacement value.
  21. ///
  22. /// This will abort on panics.
  23. #[inline]
  24. pub fn replace_with<T, F>(val: &mut T, replace: F)
  25. where F: FnOnce(T) -> T {
  26. // Guard against unwinding.
  27. let guard = ExitGuard;
  28. unsafe {
  29. // Take out the value behind the pointer.
  30. let old = ptr::read(val);
  31. // Run the closure.
  32. let new = closure(old);
  33. // Put the result back.
  34. ptr::write(val, new);
  35. }
  36. // Drop the guard.
  37. mem::forget(guard);
  38. }
  39. #[cfg(test)]
  40. mod test {
  41. use super::*;
  42. use core::cell::Cell;
  43. #[test]
  44. fn replace_with() {
  45. let mut x = Some("test");
  46. replace_with(&mut x, |_| None);
  47. assert!(x.is_none());
  48. }
  49. #[test]
  50. fn replace_with_2() {
  51. let is_called = Cell::new(false);
  52. let mut x = 2;
  53. replace_with(&mut x, |_| {
  54. is_called.set(true);
  55. 3
  56. });
  57. assert!(is_called.get());
  58. }
  59. }