1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071 |
- //! Functions for temporarily moving out of ownership.
- // TODO: https://github.com/rust-lang/rfcs/pull/1736
- use core::{mem, intrinsics};
- /// A guarding type which will exit upon drop.
- ///
- /// This is used for catching unwinding and transforming it into abort.
- struct ExitGuard;
- impl Drop for ExitGuard {
- fn drop(&mut self) {
- // To make sure the user gets a meaningful error message (as opposed to a simple abort), we
- // log to the `ERROR` level.
- log!(ERROR, "Unwinding in a `take` closure.");
- // Just abort the program.
- unsafe { intrinsics::abort(); }
- }
- }
- /// Temporarily take ownership of the inner value of a reference.
- ///
- /// This essentially works as a generalized version of `mem::replace`, which instead takes a
- /// closure that will return the replacement value.
- ///
- /// This will abort on panics.
- #[inline]
- pub fn replace_with<T, F>(val: &mut T, replace: F)
- where F: FnOnce(T) -> T {
- // Guard against unwinding.
- let guard = ExitGuard;
- unsafe {
- // Take out the value behind the pointer.
- let old = ptr::read(val);
- // Run the closure.
- let new = closure(old);
- // Put the result back.
- ptr::write(val, new);
- }
- // Drop the guard.
- mem::forget(guard);
- }
- #[cfg(test)]
- mod test {
- use super::*;
- use core::cell::Cell;
- #[test]
- fn replace_with() {
- let mut x = Some("test");
- replace_with(&mut x, |_| None);
- assert!(x.is_none());
- }
- #[test]
- fn replace_with_2() {
- let is_called = Cell::new(false);
- let mut x = 2;
- replace_with(&mut x, |_| {
- is_called.set(true);
- 3
- });
- assert!(is_called.get());
- }
- }
|