diff options
| author | Tim Neumann <mail@timnn.me> | 2017-09-17 13:18:59 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2017-09-17 13:18:59 +0200 |
| commit | a0edcb4b0384db91f159a9f8c77db3fbd098b2db (patch) | |
| tree | 783ef207d4404cba43dc540fbd3469fde79c12b5 /src/liballoc/rc.rs | |
| parent | ef227f5ffe438783db0add58208d79dd84158c06 (diff) | |
| parent | 1cbb2b3a8827a2c7d37c022da1e706229b0e23af (diff) | |
| download | rust-a0edcb4b0384db91f159a9f8c77db3fbd098b2db.tar.gz rust-a0edcb4b0384db91f159a9f8c77db3fbd098b2db.zip | |
Rollup merge of #44073 - murarth:rc-into-raw-unsized, r=alexcrichton
Implement `Arc`/`Rc` raw pointer conversions for `?Sized`
* Add `T: ?Sized` bound to {`Arc`,`Rc`}::{`from_raw`,`into_raw`}
Diffstat (limited to 'src/liballoc/rc.rs')
| -rw-r--r-- | src/liballoc/rc.rs | 42 |
1 files changed, 35 insertions, 7 deletions
diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index 58c589697f4..553980d463f 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -253,7 +253,7 @@ use core::hash::{Hash, Hasher}; use core::intrinsics::abort; use core::marker; use core::marker::Unsize; -use core::mem::{self, forget, size_of_val, uninitialized}; +use core::mem::{self, align_of_val, forget, size_of_val, uninitialized}; use core::ops::Deref; use core::ops::CoerceUnsized; use core::ptr::{self, Shared}; @@ -359,7 +359,9 @@ impl<T> Rc<T> { Err(this) } } +} +impl<T: ?Sized> Rc<T> { /// Consumes the `Rc`, returning the wrapped pointer. /// /// To avoid a memory leak the pointer must be converted back to an `Rc` using @@ -413,17 +415,21 @@ impl<T> Rc<T> { /// ``` #[stable(feature = "rc_raw", since = "1.17.0")] pub unsafe fn from_raw(ptr: *const T) -> Self { - // To find the corresponding pointer to the `RcBox` we need to subtract the offset of the - // `value` field from the pointer. + // Align the unsized value to the end of the RcBox. + // Because it is ?Sized, it will always be the last field in memory. + let align = align_of_val(&*ptr); + let layout = Layout::new::<RcBox<()>>(); + let offset = (layout.size() + layout.padding_needed_for(align)) as isize; + + // Reverse the offset to find the original RcBox. + let fake_ptr = ptr as *mut RcBox<T>; + let rc_ptr = set_data_ptr(fake_ptr, (ptr as *mut u8).offset(-offset)); - let ptr = (ptr as *const u8).offset(-offset_of!(RcBox<T>, value)); Rc { - ptr: Shared::new_unchecked(ptr as *mut u8 as *mut _) + ptr: Shared::new_unchecked(rc_ptr), } } -} -impl<T: ?Sized> Rc<T> { /// Creates a new [`Weak`][weak] pointer to this value. /// /// [weak]: struct.Weak.html @@ -1523,6 +1529,28 @@ mod tests { } #[test] + fn test_into_from_raw_unsized() { + use std::fmt::Display; + use std::string::ToString; + + let rc: Rc<str> = Rc::from("foo"); + + let ptr = Rc::into_raw(rc.clone()); + let rc2 = unsafe { Rc::from_raw(ptr) }; + + assert_eq!(unsafe { &*ptr }, "foo"); + assert_eq!(rc, rc2); + + let rc: Rc<Display> = Rc::new(123); + + let ptr = Rc::into_raw(rc.clone()); + let rc2 = unsafe { Rc::from_raw(ptr) }; + + assert_eq!(unsafe { &*ptr }.to_string(), "123"); + assert_eq!(rc2.to_string(), "123"); + } + + #[test] fn get_mut() { let mut x = Rc::new(3); *Rc::get_mut(&mut x).unwrap() = 4; |
