Skip to content

Commit 0491a23

Browse files
authored
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.
2 parents 38a959a + 651cf58 commit 0491a23

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

279341
impl<T: ?Sized> Arc<T> {
@@ -1183,6 +1245,23 @@ mod tests {
11831245
assert_eq!(Arc::try_unwrap(x), Ok(5));
11841246
}
11851247

1248+
#[test]
1249+
fn into_from_raw() {
1250+
let x = Arc::new(box "hello");
1251+
let y = x.clone();
1252+
1253+
let x_ptr = Arc::into_raw(x);
1254+
drop(y);
1255+
unsafe {
1256+
assert_eq!(**x_ptr, "hello");
1257+
1258+
let x = Arc::from_raw(x_ptr);
1259+
assert_eq!(**x, "hello");
1260+
1261+
assert_eq!(Arc::try_unwrap(x).map(|x| *x), Ok("hello"));
1262+
}
1263+
}
1264+
11861265
#[test]
11871266
fn test_cowarc_clone_make_mut() {
11881267
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
@@ -364,6 +364,68 @@ impl<T> Rc<T> {
364364
pub fn would_unwrap(this: &Self) -> bool {
365365
Rc::strong_count(&this) == 1
366366
}
367+
368+
/// Consumes the `Rc`, returning the wrapped pointer.
369+
///
370+
/// To avoid a memory leak the pointer must be converted back to an `Rc` using
371+
/// [`Rc::from_raw`][from_raw].
372+
///
373+
/// [from_raw]: struct.Rc.html#method.from_raw
374+
///
375+
/// # Examples
376+
///
377+
/// ```
378+
/// #![feature(rc_raw)]
379+
///
380+
/// use std::rc::Rc;
381+
///
382+
/// let x = Rc::new(10);
383+
/// let x_ptr = Rc::into_raw(x);
384+
/// assert_eq!(unsafe { *x_ptr }, 10);
385+
/// ```
386+
#[unstable(feature = "rc_raw", issue = "37197")]
387+
pub fn into_raw(this: Self) -> *mut T {
388+
let ptr = unsafe { &mut (**this.ptr).value as *mut _ };
389+
mem::forget(this);
390+
ptr
391+
}
392+
393+
/// Constructs an `Rc` from a raw pointer.
394+
///
395+
/// The raw pointer must have been previously returned by a call to a
396+
/// [`Rc::into_raw`][into_raw].
397+
///
398+
/// This function is unsafe because improper use may lead to memory problems. For example, a
399+
/// double-free may occur if the function is called twice on the same raw pointer.
400+
///
401+
/// [into_raw]: struct.Rc.html#method.into_raw
402+
///
403+
/// # Examples
404+
///
405+
/// ```
406+
/// #![feature(rc_raw)]
407+
///
408+
/// use std::rc::Rc;
409+
///
410+
/// let x = Rc::new(10);
411+
/// let x_ptr = Rc::into_raw(x);
412+
///
413+
/// unsafe {
414+
/// // Convert back to an `Rc` to prevent leak.
415+
/// let x = Rc::from_raw(x_ptr);
416+
/// assert_eq!(*x, 10);
417+
///
418+
/// // Further calls to `Rc::from_raw(x_ptr)` would be memory unsafe.
419+
/// }
420+
///
421+
/// // The memory was freed when `x` went out of scope above, so `x_ptr` is now dangling!
422+
/// ```
423+
#[unstable(feature = "rc_raw", issue = "37197")]
424+
pub unsafe fn from_raw(ptr: *mut T) -> Self {
425+
// To find the corresponding pointer to the `RcBox` we need to subtract the offset of the
426+
// `value` field from the pointer.
427+
Rc { ptr: Shared::new((ptr as *mut u8).offset(-offset_of!(RcBox<T>, value)) as *mut _) }
428+
}
367429
}
368430

369431
impl Rc<str> {
@@ -1287,6 +1349,23 @@ mod tests {
12871349
assert_eq!(Rc::try_unwrap(x), Ok(5));
12881350
}
12891351

1352+
#[test]
1353+
fn into_from_raw() {
1354+
let x = Rc::new(box "hello");
1355+
let y = x.clone();
1356+
1357+
let x_ptr = Rc::into_raw(x);
1358+
drop(y);
1359+
unsafe {
1360+
assert_eq!(**x_ptr, "hello");
1361+
1362+
let x = Rc::from_raw(x_ptr);
1363+
assert_eq!(**x, "hello");
1364+
1365+
assert_eq!(Rc::try_unwrap(x).map(|x| *x), Ok("hello"));
1366+
}
1367+
}
1368+
12901369
#[test]
12911370
fn get_mut() {
12921371
let mut x = Rc::new(3);

0 commit comments

Comments
 (0)