drt_chain_vm/with_shared/with_shared_mut_ref.rs
1use std::rc::Rc;
2
3/// Temporarily converts a mutable reference into a reference-counted smart pointer (`Rc`).
4///
5/// This only takes as long as the closure `f` is executed.
6///
7/// All subsequent Rc clones must be dropped before `f` terminates, otherwise the function will panic.
8///
9/// The `Clone` of the argument is not used, except to preserve memory consistency in case of failure.
10///
11/// See the `Shared` type for a safer implementation, which does not require `Clone`.
12pub fn with_shared_mut_ref<T, F, R>(t: &mut T, f: F) -> R
13where
14 T: Clone,
15 F: FnOnce(Rc<T>) -> R,
16{
17 unsafe {
18 // forcefully extract the owned object from the mut ref (unsafe)
19 let obj = std::ptr::read(t);
20
21 // wrap the owned object
22 let obj_rc = Rc::new(obj);
23
24 // the main action
25 let result = f(obj_rc.clone());
26
27 // unwrapping the owned object
28 match Rc::try_unwrap(obj_rc) {
29 Ok(obj) => {
30 // Rc unwrapped successfully
31 // no need to write the owned object back to the location given by the pointer t,
32 // because it could not have changed in the mean time, it is already there
33
34 // though readonly, the object might have changed via cells,
35 // so it needs to be copied back
36 std::ptr::write(t, obj);
37 },
38 Err(obj_rc) => {
39 // could not unwrap, this means there are still references to obj elsewhere
40 // to avoid memory corruption, we perform a clone of the contents
41 let obj = (*obj_rc).clone();
42 std::ptr::write(t, obj);
43 panic!("failed to recover owned object from Rc")
44 },
45 }
46
47 result
48 }
49}
50
51#[cfg(test)]
52mod test {
53 use std::cell::RefCell;
54
55 use super::with_shared_mut_ref;
56
57 #[test]
58 fn test_with_shared_mut_ref_1() {
59 let mut s = "test string".to_string();
60 let l = with_shared_mut_ref(&mut s, |s_rc| s_rc.len());
61 assert_eq!(s.len(), l);
62 }
63
64 #[test]
65 fn test_with_shared_mut_ref_2() {
66 let mut s = RefCell::new("test string".to_string());
67 with_shared_mut_ref(&mut s, |s_rc| {
68 s_rc.borrow_mut().push_str(" ... changed");
69 });
70 assert_eq!(s.borrow().as_str(), "test string ... changed");
71 }
72
73 #[test]
74 #[should_panic = "failed to recover owned object from Rc"]
75 fn test_with_shared_mut_ref_fail() {
76 let mut s = "test string".to_string();
77 let _illegally_extracted_rc = with_shared_mut_ref(&mut s, |s_rc| s_rc);
78 }
79}