Skip to content

Commit 3977133

Browse files
author
Stjepan Glavina
committed
Allow unsized types in mem::drop and mem::forget
1 parent af791bb commit 3977133

File tree

5 files changed

+126
-2
lines changed

5 files changed

+126
-2
lines changed

src/libcore/intrinsics.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -714,6 +714,10 @@ extern "rust-intrinsic" {
714714
/// initialize memory previous set to the result of `uninit`.
715715
pub fn uninit<T>() -> T;
716716

717+
/// Moves a value out of scope without running drop glue.
718+
#[cfg(not(stage0))]
719+
pub fn forget<T>(_: T);
720+
717721
/// Reinterprets the bits of a value of one type as another type.
718722
///
719723
/// Both types must have the same size. Neither the original, nor the result,

src/libcore/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@
107107
#![feature(staged_api)]
108108
#![feature(stmt_expr_attributes)]
109109
#![feature(unboxed_closures)]
110+
#![feature(unsized_locals)]
110111
#![feature(untagged_unions)]
111112
#![feature(unwind_attributes)]
112113
#![feature(doc_alias)]

src/libcore/mem.rs

Lines changed: 119 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,124 @@ pub use intrinsics::transmute;
139139
/// [ub]: ../../reference/behavior-considered-undefined.html
140140
#[inline]
141141
#[stable(feature = "rust1", since = "1.0.0")]
142+
#[cfg(not(stage0))]
143+
pub fn forget<T: ?Sized>(t: T) {
144+
unsafe { intrinsics::forget(t) }
145+
}
146+
147+
/// Takes ownership and "forgets" about the value **without running its destructor**.
148+
///
149+
/// Any resources the value manages, such as heap memory or a file handle, will linger
150+
/// forever in an unreachable state. However, it does not guarantee that pointers
151+
/// to this memory will remain valid.
152+
///
153+
/// * If you want to leak memory, see [`Box::leak`][leak].
154+
/// * If you want to obtain a raw pointer to the memory, see [`Box::into_raw`][into_raw].
155+
/// * If you want to dispose of a value properly, running its destructor, see
156+
/// [`mem::drop`][drop].
157+
///
158+
/// # Safety
159+
///
160+
/// `forget` is not marked as `unsafe`, because Rust's safety guarantees
161+
/// do not include a guarantee that destructors will always run. For example,
162+
/// a program can create a reference cycle using [`Rc`][rc], or call
163+
/// [`process::exit`][exit] to exit without running destructors. Thus, allowing
164+
/// `mem::forget` from safe code does not fundamentally change Rust's safety
165+
/// guarantees.
166+
///
167+
/// That said, leaking resources such as memory or I/O objects is usually undesirable,
168+
/// so `forget` is only recommended for specialized use cases like those shown below.
169+
///
170+
/// Because forgetting a value is allowed, any `unsafe` code you write must
171+
/// allow for this possibility. You cannot return a value and expect that the
172+
/// caller will necessarily run the value's destructor.
173+
///
174+
/// [rc]: ../../std/rc/struct.Rc.html
175+
/// [exit]: ../../std/process/fn.exit.html
176+
///
177+
/// # Examples
178+
///
179+
/// Leak an I/O object, never closing the file:
180+
///
181+
/// ```no_run
182+
/// use std::mem;
183+
/// use std::fs::File;
184+
///
185+
/// let file = File::open("foo.txt").unwrap();
186+
/// mem::forget(file);
187+
/// ```
188+
///
189+
/// The practical use cases for `forget` are rather specialized and mainly come
190+
/// up in unsafe or FFI code.
191+
///
192+
/// ## Use case 1
193+
///
194+
/// You have created an uninitialized value using [`mem::uninitialized`][uninit].
195+
/// You must either initialize or `forget` it on every computation path before
196+
/// Rust drops it automatically, like at the end of a scope or after a panic.
197+
/// Running the destructor on an uninitialized value would be [undefined behavior][ub].
198+
///
199+
/// ```
200+
/// use std::mem;
201+
/// use std::ptr;
202+
///
203+
/// # let some_condition = false;
204+
/// unsafe {
205+
/// let mut uninit_vec: Vec<u32> = mem::uninitialized();
206+
///
207+
/// if some_condition {
208+
/// // Initialize the variable.
209+
/// ptr::write(&mut uninit_vec, Vec::new());
210+
/// } else {
211+
/// // Forget the uninitialized value so its destructor doesn't run.
212+
/// mem::forget(uninit_vec);
213+
/// }
214+
/// }
215+
/// ```
216+
///
217+
/// ## Use case 2
218+
///
219+
/// You have duplicated the bytes making up a value, without doing a proper
220+
/// [`Clone`][clone]. You need the value's destructor to run only once,
221+
/// because a double `free` is undefined behavior.
222+
///
223+
/// An example is a possible implementation of [`mem::swap`][swap]:
224+
///
225+
/// ```
226+
/// use std::mem;
227+
/// use std::ptr;
228+
///
229+
/// # #[allow(dead_code)]
230+
/// fn swap<T>(x: &mut T, y: &mut T) {
231+
/// unsafe {
232+
/// // Give ourselves some scratch space to work with
233+
/// let mut t: T = mem::uninitialized();
234+
///
235+
/// // Perform the swap, `&mut` pointers never alias
236+
/// ptr::copy_nonoverlapping(&*x, &mut t, 1);
237+
/// ptr::copy_nonoverlapping(&*y, x, 1);
238+
/// ptr::copy_nonoverlapping(&t, y, 1);
239+
///
240+
/// // y and t now point to the same thing, but we need to completely
241+
/// // forget `t` because we do not want to run the destructor for `T`
242+
/// // on its value, which is still owned somewhere outside this function.
243+
/// mem::forget(t);
244+
/// }
245+
/// }
246+
/// ```
247+
///
248+
/// [drop]: fn.drop.html
249+
/// [uninit]: fn.uninitialized.html
250+
/// [clone]: ../clone/trait.Clone.html
251+
/// [swap]: fn.swap.html
252+
/// [FFI]: ../../book/first-edition/ffi.html
253+
/// [box]: ../../std/boxed/struct.Box.html
254+
/// [leak]: ../../std/boxed/struct.Box.html#method.leak
255+
/// [into_raw]: ../../std/boxed/struct.Box.html#method.into_raw
256+
/// [ub]: ../../reference/behavior-considered-undefined.html
257+
#[inline]
258+
#[cfg(stage0)]
259+
#[stable(feature = "rust1", since = "1.0.0")]
142260
pub fn forget<T>(t: T) {
143261
ManuallyDrop::new(t);
144262
}
@@ -763,7 +881,7 @@ pub fn replace<T>(dest: &mut T, mut src: T) -> T {
763881
/// [`Copy`]: ../../std/marker/trait.Copy.html
764882
#[inline]
765883
#[stable(feature = "rust1", since = "1.0.0")]
766-
pub fn drop<T>(_x: T) { }
884+
pub fn drop<T: ?Sized>(_x: T) { }
767885

768886
/// Interprets `src` as having type `&U`, and then reads `src` without moving
769887
/// the contained value.

src/librustc_codegen_llvm/intrinsic.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ pub fn codegen_intrinsic_call(
193193
return;
194194
}
195195
// Effectively no-ops
196-
"uninit" => {
196+
"uninit" | "forget" => {
197197
return;
198198
}
199199
"needs_drop" => {

src/librustc_typeck/check/intrinsic.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ pub fn check_intrinsic_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
134134
"rustc_peek" => (1, vec![param(0)], param(0)),
135135
"init" => (1, Vec::new(), param(0)),
136136
"uninit" => (1, Vec::new(), param(0)),
137+
"forget" => (1, vec![param(0)], param(0)),
137138
"transmute" => (2, vec![ param(0) ], param(1)),
138139
"move_val_init" => {
139140
(1,

0 commit comments

Comments
 (0)