Skip to content

Commit 651cf58

Browse files
committed
Add {into,from}_raw to Rc and Arc
1 parent d34318d commit 651cf58

File tree

4 files changed

+190
-0
lines changed

4 files changed

+190
-0
lines changed

src/liballoc/arc.rs

+79
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,68 @@ impl<T> Arc<T> {
270270
Ok(elem)
271271
}
272272
}
273+
274+
/// Consumes the `Arc`, returning the wrapped pointer.
275+
///
276+
/// To avoid a memory leak the pointer must be converted back to an `Arc` using
277+
/// [`Arc::from_raw`][from_raw].
278+
///
279+
/// [from_raw]: struct.Arc.html#method.from_raw
280+
///
281+
/// # Examples
282+
///
283+
/// ```
284+
/// #![feature(rc_raw)]
285+
///
286+
/// use std::sync::Arc;
287+
///
288+
/// let x = Arc::new(10);
289+
/// let x_ptr = Arc::into_raw(x);
290+
/// assert_eq!(unsafe { *x_ptr }, 10);
291+
/// ```
292+
#[unstable(feature = "rc_raw", issue = "37197")]
293+
pub fn into_raw(this: Self) -> *mut T {
294+
let ptr = unsafe { &mut (**this.ptr).data as *mut _ };
295+
mem::forget(this);
296+
ptr
297+
}
298+
299+
/// Constructs an `Arc` from a raw pointer.
300+
///
301+
/// The raw pointer must have been previously returned by a call to a
302+
/// [`Arc::into_raw`][into_raw].
303+
///
304+
/// This function is unsafe because improper use may lead to memory problems. For example, a
305+
/// double-free may occur if the function is called twice on the same raw pointer.
306+
///
307+
/// [into_raw]: struct.Arc.html#method.into_raw
308+
///
309+
/// # Examples
310+
///
311+
/// ```
312+
/// #![feature(rc_raw)]
313+
///
314+
/// use std::sync::Arc;
315+
///
316+
/// let x = Arc::new(10);
317+
/// let x_ptr = Arc::into_raw(x);
318+
///
319+
/// unsafe {
320+
/// // Convert back to an `Arc` to prevent leak.
321+
/// let x = Arc::from_raw(x_ptr);
322+
/// assert_eq!(*x, 10);
323+
///
324+
/// // Further calls to `Arc::from_raw(x_ptr)` would be memory unsafe.
325+
/// }
326+
///
327+
/// // The memory was freed when `x` went out of scope above, so `x_ptr` is now dangling!
328+
/// ```
329+
#[unstable(feature = "rc_raw", issue = "37197")]
330+
pub unsafe fn from_raw(ptr: *mut T) -> Self {
331+
// To find the corresponding pointer to the `ArcInner` we need to subtract the offset of the
332+
// `data` field from the pointer.
333+
Arc { ptr: Shared::new((ptr as *mut u8).offset(-offset_of!(ArcInner<T>, data)) as *mut _) }
334+
}
273335
}
274336

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

1244+
#[test]
1245+
fn into_from_raw() {
1246+
let x = Arc::new(box "hello");
1247+
let y = x.clone();
1248+
1249+
let x_ptr = Arc::into_raw(x);
1250+
drop(y);
1251+
unsafe {
1252+
assert_eq!(**x_ptr, "hello");
1253+
1254+
let x = Arc::from_raw(x_ptr);
1255+
assert_eq!(**x, "hello");
1256+
1257+
assert_eq!(Arc::try_unwrap(x).map(|x| *x), Ok("hello"));
1258+
}
1259+
}
1260+
11821261
#[test]
11831262
fn test_cowarc_clone_make_mut() {
11841263
let mut cow0 = Arc::new(75);

src/liballoc/lib.rs

+4
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@
9999
#[macro_use]
100100
extern crate std;
101101

102+
// Module with internal macros used by other modules (needs to be included before other modules).
103+
#[macro_use]
104+
mod macros;
105+
102106
// Heaps provided for low-level allocation strategies
103107

104108
pub mod heap;

src/liballoc/macros.rs

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright 2013-2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Private macro to get the offset of a struct field in bytes from the address of the struct.
12+
macro_rules! offset_of {
13+
($container:path, $field:ident) => {{
14+
// Make sure the field actually exists. This line ensures that a compile-time error is
15+
// generated if $field is accessed through a Deref impl.
16+
let $container { $field : _, .. };
17+
18+
// Create an (invalid) instance of the container and calculate the offset to its
19+
// field. Using a null pointer might be UB if `&(*(0 as *const T)).field` is interpreted to
20+
// be nullptr deref.
21+
let invalid: $container = ::core::mem::uninitialized();
22+
let offset = &invalid.$field as *const _ as usize - &invalid as *const _ as usize;
23+
24+
// Do not run destructors on the made up invalid instance.
25+
::core::mem::forget(invalid);
26+
offset as isize
27+
}};
28+
}

src/liballoc/rc.rs

+79
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,68 @@ impl<T> Rc<T> {
363363
pub fn would_unwrap(this: &Self) -> bool {
364364
Rc::strong_count(&this) == 1
365365
}
366+
367+
/// Consumes the `Rc`, returning the wrapped pointer.
368+
///
369+
/// To avoid a memory leak the pointer must be converted back to an `Rc` using
370+
/// [`Rc::from_raw`][from_raw].
371+
///
372+
/// [from_raw]: struct.Rc.html#method.from_raw
373+
///
374+
/// # Examples
375+
///
376+
/// ```
377+
/// #![feature(rc_raw)]
378+
///
379+
/// use std::rc::Rc;
380+
///
381+
/// let x = Rc::new(10);
382+
/// let x_ptr = Rc::into_raw(x);
383+
/// assert_eq!(unsafe { *x_ptr }, 10);
384+
/// ```
385+
#[unstable(feature = "rc_raw", issue = "37197")]
386+
pub fn into_raw(this: Self) -> *mut T {
387+
let ptr = unsafe { &mut (**this.ptr).value as *mut _ };
388+
mem::forget(this);
389+
ptr
390+
}
391+
392+
/// Constructs an `Rc` from a raw pointer.
393+
///
394+
/// The raw pointer must have been previously returned by a call to a
395+
/// [`Rc::into_raw`][into_raw].
396+
///
397+
/// This function is unsafe because improper use may lead to memory problems. For example, a
398+
/// double-free may occur if the function is called twice on the same raw pointer.
399+
///
400+
/// [into_raw]: struct.Rc.html#method.into_raw
401+
///
402+
/// # Examples
403+
///
404+
/// ```
405+
/// #![feature(rc_raw)]
406+
///
407+
/// use std::rc::Rc;
408+
///
409+
/// let x = Rc::new(10);
410+
/// let x_ptr = Rc::into_raw(x);
411+
///
412+
/// unsafe {
413+
/// // Convert back to an `Rc` to prevent leak.
414+
/// let x = Rc::from_raw(x_ptr);
415+
/// assert_eq!(*x, 10);
416+
///
417+
/// // Further calls to `Rc::from_raw(x_ptr)` would be memory unsafe.
418+
/// }
419+
///
420+
/// // The memory was freed when `x` went out of scope above, so `x_ptr` is now dangling!
421+
/// ```
422+
#[unstable(feature = "rc_raw", issue = "37197")]
423+
pub unsafe fn from_raw(ptr: *mut T) -> Self {
424+
// To find the corresponding pointer to the `RcBox` we need to subtract the offset of the
425+
// `value` field from the pointer.
426+
Rc { ptr: Shared::new((ptr as *mut u8).offset(-offset_of!(RcBox<T>, value)) as *mut _) }
427+
}
366428
}
367429

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

1326+
#[test]
1327+
fn into_from_raw() {
1328+
let x = Rc::new(box "hello");
1329+
let y = x.clone();
1330+
1331+
let x_ptr = Rc::into_raw(x);
1332+
drop(y);
1333+
unsafe {
1334+
assert_eq!(**x_ptr, "hello");
1335+
1336+
let x = Rc::from_raw(x_ptr);
1337+
assert_eq!(**x, "hello");
1338+
1339+
assert_eq!(Rc::try_unwrap(x).map(|x| *x), Ok("hello"));
1340+
}
1341+
}
1342+
12641343
#[test]
12651344
fn get_mut() {
12661345
let mut x = Rc::new(3);

0 commit comments

Comments
 (0)