replace_map/lib.rs
1#![feature(core)]
2#![cfg_attr(test, feature(std_misc))]
3
4#![deny(missing_docs)]
5#![cfg_attr(test, deny(warnings))]
6
7
8//! Exposes `replace_map`, for replacing values at mutable memory locations.
9
10use std::ptr;
11
12/// Replace the value at a mutable memory location with the value
13/// produced by the passed in closure.
14///
15/// Does not create an intermediate value, so is more efficient and
16/// ergonomic in cases where producing a value to pass to mem::replace
17/// is hard.
18///
19/// This is not a totally safe function, it has some curious edge cases.
20/// Generally you should only pass in a reference the data behind
21/// that reference owns itself. An example may be helpful:
22///
23/// Unsafe Usage:
24///
25/// ```ignore
26/// struct Foo { num: Box<u32> }
27/// struct Bar { foo: Foo }
28///
29/// impl Drop for Bar {
30/// fn drop(&mut self) {
31/// *self.foo.num
32/// }
33/// }
34///
35/// let mut b = Bar { foo: Foo { num: Box::new(123) } };
36///
37/// // replace_map will zero b.foo.num when it reads it and passes
38/// // it to the closure. The closure then panics, leaving b.foo.num
39/// // set to 0, which then causes a null-pointer dereference in b's
40/// // destructors.
41/// unsafe { replace_map(&mut b.foo.num, |_| panic!()); }
42/// ```
43pub unsafe fn replace_map<'a, T, F>(src: &mut T, prod: F)
44where F: FnOnce(T) -> T {
45 // Read the value, pass it to prod, then write-over src.
46 *src = prod(ptr::read_and_zero(src as *mut T));
47}
48
49#[test] fn test_works() {
50 let mut a = 7;
51 let b = &mut a;
52
53 unsafe { replace_map(b, |x: usize| x * 2) };
54 assert_eq!(*b, 14);
55}
56
57#[test] fn is_partially_panic_safe() {
58 static mut DROP_COUNT: usize = 0;
59 struct Dropper;
60
61 impl Drop for Dropper {
62 fn drop(&mut self) {
63 unsafe { DROP_COUNT += 1 }
64 }
65 }
66
67 std::thread::Thread::scoped(move || {
68 let mut a = Dropper;
69 let b = &mut a;
70
71 unsafe { replace_map(b, |_| panic!("Muahaha")); }
72 }).join().unwrap_err();
73
74 assert_eq!(unsafe { DROP_COUNT }, 1);
75}
76