Skip to content

Commit

Permalink
Add {into,from}_raw to Rc and Arc
Browse files Browse the repository at this point in the history
  • Loading branch information
cristicbz committed Nov 5, 2016
1 parent d34318d commit 651cf58
Show file tree
Hide file tree
Showing 4 changed files with 190 additions and 0 deletions.
79 changes: 79 additions & 0 deletions src/liballoc/arc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,68 @@ impl<T> Arc<T> {
Ok(elem)
}
}

/// Consumes the `Arc`, returning the wrapped pointer.
///
/// To avoid a memory leak the pointer must be converted back to an `Arc` using
/// [`Arc::from_raw`][from_raw].
///
/// [from_raw]: struct.Arc.html#method.from_raw
///
/// # Examples
///
/// ```
/// #![feature(rc_raw)]
///
/// use std::sync::Arc;
///
/// let x = Arc::new(10);
/// let x_ptr = Arc::into_raw(x);
/// assert_eq!(unsafe { *x_ptr }, 10);
/// ```
#[unstable(feature = "rc_raw", issue = "37197")]
pub fn into_raw(this: Self) -> *mut T {
let ptr = unsafe { &mut (**this.ptr).data as *mut _ };
mem::forget(this);
ptr
}

/// Constructs an `Arc` from a raw pointer.
///
/// The raw pointer must have been previously returned by a call to a
/// [`Arc::into_raw`][into_raw].
///
/// This function is unsafe because improper use may lead to memory problems. For example, a
/// double-free may occur if the function is called twice on the same raw pointer.
///
/// [into_raw]: struct.Arc.html#method.into_raw
///
/// # Examples
///
/// ```
/// #![feature(rc_raw)]
///
/// use std::sync::Arc;
///
/// let x = Arc::new(10);
/// let x_ptr = Arc::into_raw(x);
///
/// unsafe {
/// // Convert back to an `Arc` to prevent leak.
/// let x = Arc::from_raw(x_ptr);
/// assert_eq!(*x, 10);
///
/// // Further calls to `Arc::from_raw(x_ptr)` would be memory unsafe.
/// }
///
/// // The memory was freed when `x` went out of scope above, so `x_ptr` is now dangling!
/// ```
#[unstable(feature = "rc_raw", issue = "37197")]
pub unsafe fn from_raw(ptr: *mut T) -> Self {
// To find the corresponding pointer to the `ArcInner` we need to subtract the offset of the
// `data` field from the pointer.
Arc { ptr: Shared::new((ptr as *mut u8).offset(-offset_of!(ArcInner<T>, data)) as *mut _) }
}
}

impl<T: ?Sized> Arc<T> {
Expand Down Expand Up @@ -1179,6 +1241,23 @@ mod tests {
assert_eq!(Arc::try_unwrap(x), Ok(5));
}

#[test]
fn into_from_raw() {
let x = Arc::new(box "hello");
let y = x.clone();

let x_ptr = Arc::into_raw(x);
drop(y);
unsafe {
assert_eq!(**x_ptr, "hello");

let x = Arc::from_raw(x_ptr);
assert_eq!(**x, "hello");

assert_eq!(Arc::try_unwrap(x).map(|x| *x), Ok("hello"));
}
}

#[test]
fn test_cowarc_clone_make_mut() {
let mut cow0 = Arc::new(75);
Expand Down
4 changes: 4 additions & 0 deletions src/liballoc/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@
#[macro_use]
extern crate std;

// Module with internal macros used by other modules (needs to be included before other modules).
#[macro_use]
mod macros;

// Heaps provided for low-level allocation strategies

pub mod heap;
Expand Down
28 changes: 28 additions & 0 deletions src/liballoc/macros.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright 2013-2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// Private macro to get the offset of a struct field in bytes from the address of the struct.
macro_rules! offset_of {
($container:path, $field:ident) => {{
// Make sure the field actually exists. This line ensures that a compile-time error is
// generated if $field is accessed through a Deref impl.
let $container { $field : _, .. };

// Create an (invalid) instance of the container and calculate the offset to its
// field. Using a null pointer might be UB if `&(*(0 as *const T)).field` is interpreted to
// be nullptr deref.
let invalid: $container = ::core::mem::uninitialized();
let offset = &invalid.$field as *const _ as usize - &invalid as *const _ as usize;

// Do not run destructors on the made up invalid instance.
::core::mem::forget(invalid);
offset as isize
}};
}
79 changes: 79 additions & 0 deletions src/liballoc/rc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,68 @@ impl<T> Rc<T> {
pub fn would_unwrap(this: &Self) -> bool {
Rc::strong_count(&this) == 1
}

/// Consumes the `Rc`, returning the wrapped pointer.
///
/// To avoid a memory leak the pointer must be converted back to an `Rc` using
/// [`Rc::from_raw`][from_raw].
///
/// [from_raw]: struct.Rc.html#method.from_raw
///
/// # Examples
///
/// ```
/// #![feature(rc_raw)]
///
/// use std::rc::Rc;
///
/// let x = Rc::new(10);
/// let x_ptr = Rc::into_raw(x);
/// assert_eq!(unsafe { *x_ptr }, 10);
/// ```
#[unstable(feature = "rc_raw", issue = "37197")]
pub fn into_raw(this: Self) -> *mut T {
let ptr = unsafe { &mut (**this.ptr).value as *mut _ };
mem::forget(this);
ptr
}

/// Constructs an `Rc` from a raw pointer.
///
/// The raw pointer must have been previously returned by a call to a
/// [`Rc::into_raw`][into_raw].
///
/// This function is unsafe because improper use may lead to memory problems. For example, a
/// double-free may occur if the function is called twice on the same raw pointer.
///
/// [into_raw]: struct.Rc.html#method.into_raw
///
/// # Examples
///
/// ```
/// #![feature(rc_raw)]
///
/// use std::rc::Rc;
///
/// let x = Rc::new(10);
/// let x_ptr = Rc::into_raw(x);
///
/// unsafe {
/// // Convert back to an `Rc` to prevent leak.
/// let x = Rc::from_raw(x_ptr);
/// assert_eq!(*x, 10);
///
/// // Further calls to `Rc::from_raw(x_ptr)` would be memory unsafe.
/// }
///
/// // The memory was freed when `x` went out of scope above, so `x_ptr` is now dangling!
/// ```
#[unstable(feature = "rc_raw", issue = "37197")]
pub unsafe fn from_raw(ptr: *mut T) -> Self {
// To find the corresponding pointer to the `RcBox` we need to subtract the offset of the
// `value` field from the pointer.
Rc { ptr: Shared::new((ptr as *mut u8).offset(-offset_of!(RcBox<T>, value)) as *mut _) }
}
}

impl<T: ?Sized> Rc<T> {
Expand Down Expand Up @@ -1261,6 +1323,23 @@ mod tests {
assert_eq!(Rc::try_unwrap(x), Ok(5));
}

#[test]
fn into_from_raw() {
let x = Rc::new(box "hello");
let y = x.clone();

let x_ptr = Rc::into_raw(x);
drop(y);
unsafe {
assert_eq!(**x_ptr, "hello");

let x = Rc::from_raw(x_ptr);
assert_eq!(**x, "hello");

assert_eq!(Rc::try_unwrap(x).map(|x| *x), Ok("hello"));
}
}

#[test]
fn get_mut() {
let mut x = Rc::new(3);
Expand Down

0 comments on commit 651cf58

Please sign in to comment.