Auto merge of #37192 - cristicbz:rust-rc-into-raw, r=brson
Add `{into,from}_raw` to Rc and Arc
These methods convert to and from a `*const T` for `Rc` and `Arc` similar to the way they work on `Box`. The only slight complication is that `from_raw` needs to offset the pointer back to find the beginning of the `RcBox`/`ArcInner`.
I felt this is a fairly small addition, filling in a gap (when compared to `Box`) so it wouldn't need an RFC. The motivation is primarily for FFI.
(I'll create an issue and update a PR with the issue number if reviewers agree with the change in principle **Edit: done #37197**)
~~Edit: This was initially `{into,from}_raw` but concerns were raised about the possible footgun if mixed with the methods of the same name of `Box`.~~
Edit: This was went from `{into,from}_raw` to `{into,from}_inner_raw` then back to `{into,from}_raw` during review.
This commit is contained in:
@@ -274,6 +274,68 @@ impl<T> Arc<T> {
|
|||||||
Ok(elem)
|
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> {
|
impl<T: ?Sized> Arc<T> {
|
||||||
@@ -1183,6 +1245,23 @@ mod tests {
|
|||||||
assert_eq!(Arc::try_unwrap(x), Ok(5));
|
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]
|
#[test]
|
||||||
fn test_cowarc_clone_make_mut() {
|
fn test_cowarc_clone_make_mut() {
|
||||||
let mut cow0 = Arc::new(75);
|
let mut cow0 = Arc::new(75);
|
||||||
|
|||||||
@@ -99,6 +99,10 @@
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate std;
|
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
|
// Heaps provided for low-level allocation strategies
|
||||||
|
|
||||||
pub mod heap;
|
pub mod heap;
|
||||||
|
|||||||
28
src/liballoc/macros.rs
Normal file
28
src/liballoc/macros.rs
Normal file
@@ -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
|
||||||
|
}};
|
||||||
|
}
|
||||||
@@ -364,6 +364,68 @@ impl<T> Rc<T> {
|
|||||||
pub fn would_unwrap(this: &Self) -> bool {
|
pub fn would_unwrap(this: &Self) -> bool {
|
||||||
Rc::strong_count(&this) == 1
|
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 Rc<str> {
|
impl Rc<str> {
|
||||||
@@ -1287,6 +1349,23 @@ mod tests {
|
|||||||
assert_eq!(Rc::try_unwrap(x), Ok(5));
|
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]
|
#[test]
|
||||||
fn get_mut() {
|
fn get_mut() {
|
||||||
let mut x = Rc::new(3);
|
let mut x = Rc::new(3);
|
||||||
|
|||||||
Reference in New Issue
Block a user