Skip to content

Add {into,from}_raw to Rc and Arc #37192

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 8, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 : _, .. };
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could've used ref $field to avoid using field access below (came here from the tracking issue which I found from the docs).


// 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