Skip to content

Commit ee771bf

Browse files
authored
Rollup merge of rust-lang#34609 - ubsan:transmute_docs, r=steveklabnik
Add more docs - mostly warnings - to std::mem::transmute
2 parents 48fda61 + 24f8589 commit ee771bf

File tree

1 file changed

+188
-5
lines changed

1 file changed

+188
-5
lines changed

src/libcore/intrinsics.rs

Lines changed: 188 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -277,17 +277,200 @@ extern "rust-intrinsic" {
277277
/// Moves a value out of scope without running drop glue.
278278
pub fn forget<T>(_: T) -> ();
279279

280-
/// Unsafely transforms a value of one type into a value of another type.
280+
/// Reinterprets the bits of a value of one type as another type; both types
281+
/// must have the same size. Neither the original, nor the result, may be an
282+
/// [invalid value] (../../nomicon/meet-safe-and-unsafe.html).
281283
///
282-
/// Both types must have the same size.
284+
/// `transmute` is semantically equivalent to a bitwise move of one type
285+
/// into another. It copies the bits from the destination type into the
286+
/// source type, then forgets the original. It's equivalent to C's `memcpy`
287+
/// under the hood, just like `transmute_copy`.
288+
///
289+
/// `transmute` is incredibly unsafe. There are a vast number of ways to
290+
/// cause undefined behavior with this function. `transmute` should be
291+
/// the absolute last resort.
292+
///
293+
/// The [nomicon](../../nomicon/transmutes.html) has additional
294+
/// documentation.
283295
///
284296
/// # Examples
285297
///
298+
/// There are a few things that `transmute` is really useful for.
299+
///
300+
/// Getting the bitpattern of a floating point type (or, more generally,
301+
/// type punning, when `T` and `U` aren't pointers):
302+
///
286303
/// ```
287-
/// use std::mem;
304+
/// let bitpattern = unsafe {
305+
/// std::mem::transmute::<f32, u32>(1.0)
306+
/// };
307+
/// assert_eq!(bitpattern, 0x3F800000);
308+
/// ```
309+
///
310+
/// Turning a pointer into a function pointer:
311+
///
312+
/// ```
313+
/// fn foo() -> i32 {
314+
/// 0
315+
/// }
316+
/// let pointer = foo as *const ();
317+
/// let function = unsafe {
318+
/// std::mem::transmute::<*const (), fn() -> i32>(pointer)
319+
/// };
320+
/// assert_eq!(function(), 0);
321+
/// ```
322+
///
323+
/// Extending a lifetime, or shortening an invariant lifetime; this is
324+
/// advanced, very unsafe rust:
325+
///
326+
/// ```
327+
/// struct R<'a>(&'a i32);
328+
/// unsafe fn extend_lifetime<'b>(r: R<'b>) -> R<'static> {
329+
/// std::mem::transmute::<R<'b>, R<'static>>(r)
330+
/// }
331+
///
332+
/// unsafe fn shorten_invariant_lifetime<'b, 'c>(r: &'b mut R<'static>)
333+
/// -> &'b mut R<'c> {
334+
/// std::mem::transmute::<&'b mut R<'static>, &'b mut R<'c>>(r)
335+
/// }
336+
/// ```
337+
///
338+
/// # Alternatives
339+
///
340+
/// However, many uses of `transmute` can be achieved through other means.
341+
/// `transmute` can transform any type into any other, with just the caveat
342+
/// that they're the same size, and often interesting results occur. Below
343+
/// are common applications of `transmute` which can be replaced with safe
344+
/// applications of `as`:
288345
///
289-
/// let array: &[u8] = unsafe { mem::transmute("Rust") };
290-
/// assert_eq!(array, [82, 117, 115, 116]);
346+
/// Turning a pointer into a `usize`:
347+
///
348+
/// ```
349+
/// let ptr = &0;
350+
/// let ptr_num_transmute = unsafe {
351+
/// std::mem::transmute::<&i32, usize>(ptr)
352+
/// };
353+
/// // Use an `as` cast instead
354+
/// let ptr_num_cast = ptr as *const i32 as usize;
355+
/// ```
356+
///
357+
/// Turning a `*mut T` into an `&mut T`:
358+
///
359+
/// ```
360+
/// let ptr: *mut i32 = &mut 0;
361+
/// let ref_transmuted = unsafe {
362+
/// std::mem::transmute::<*mut i32, &mut i32>(ptr)
363+
/// };
364+
/// // Use a reborrow instead
365+
/// let ref_casted = unsafe { &mut *ptr };
366+
/// ```
367+
///
368+
/// Turning an `&mut T` into an `&mut U`:
369+
///
370+
/// ```
371+
/// let ptr = &mut 0;
372+
/// let val_transmuted = unsafe {
373+
/// std::mem::transmute::<&mut i32, &mut u32>(ptr)
374+
/// };
375+
/// // Now, put together `as` and reborrowing - note the chaining of `as`
376+
/// // `as` is not transitive
377+
/// let val_casts = unsafe { &mut *(ptr as *mut i32 as *mut u32) };
378+
/// ```
379+
///
380+
/// Turning an `&str` into an `&[u8]`:
381+
///
382+
/// ```
383+
/// // this is not a good way to do this.
384+
/// let slice = unsafe { std::mem::transmute::<&str, &[u8]>("Rust") };
385+
/// assert_eq!(slice, &[82, 117, 115, 116]);
386+
/// // You could use `str::as_bytes`
387+
/// let slice = "Rust".as_bytes();
388+
/// assert_eq!(slice, &[82, 117, 115, 116]);
389+
/// // Or, just use a byte string, if you have control over the string
390+
/// // literal
391+
/// assert_eq!(b"Rust", &[82, 117, 115, 116]);
392+
/// ```
393+
///
394+
/// Turning a `Vec<&T>` into a `Vec<Option<&T>>`:
395+
///
396+
/// ```
397+
/// let store = [0, 1, 2, 3];
398+
/// let mut v_orig = store.iter().collect::<Vec<&i32>>();
399+
/// // Using transmute: this is Undefined Behavior, and a bad idea.
400+
/// // However, it is no-copy.
401+
/// let v_transmuted = unsafe {
402+
/// std::mem::transmute::<Vec<&i32>, Vec<Option<&i32>>>(
403+
/// v_orig.clone())
404+
/// };
405+
/// // This is the suggested, safe way.
406+
/// // It does copy the entire Vector, though, into a new array.
407+
/// let v_collected = v_orig.clone()
408+
/// .into_iter()
409+
/// .map(|r| Some(r))
410+
/// .collect::<Vec<Option<&i32>>>();
411+
/// // The no-copy, unsafe way, still using transmute, but not UB.
412+
/// // This is equivalent to the original, but safer, and reuses the
413+
/// // same Vec internals. Therefore the new inner type must have the
414+
/// // exact same size, and the same or lesser alignment, as the old
415+
/// // type. The same caveats exist for this method as transmute, for
416+
/// // the original inner type (`&i32`) to the converted inner type
417+
/// // (`Option<&i32>`), so read the nomicon pages linked above.
418+
/// let v_from_raw = unsafe {
419+
/// Vec::from_raw_parts(v_orig.as_mut_ptr(),
420+
/// v_orig.len(),
421+
/// v_orig.capacity())
422+
/// };
423+
/// std::mem::forget(v_orig);
424+
/// ```
425+
///
426+
/// Implementing `split_at_mut`:
427+
///
428+
/// ```
429+
/// use std::{slice, mem};
430+
/// // There are multiple ways to do this; and there are multiple problems
431+
/// // with the following, transmute, way.
432+
/// fn split_at_mut_transmute<T>(slice: &mut [T], mid: usize)
433+
/// -> (&mut [T], &mut [T]) {
434+
/// let len = slice.len();
435+
/// assert!(mid <= len);
436+
/// unsafe {
437+
/// let slice2 = mem::transmute::<&mut [T], &mut [T]>(slice);
438+
/// // first: transmute is not typesafe; all it checks is that T and
439+
/// // U are of the same size. Second, right here, you have two
440+
/// // mutable references pointing to the same memory.
441+
/// (&mut slice[0..mid], &mut slice2[mid..len])
442+
/// }
443+
/// }
444+
/// // This gets rid of the typesafety problems; `&mut *` will *only* give
445+
/// // you an `&mut T` from an `&mut T` or `*mut T`.
446+
/// fn split_at_mut_casts<T>(slice: &mut [T], mid: usize)
447+
/// -> (&mut [T], &mut [T]) {
448+
/// let len = slice.len();
449+
/// assert!(mid <= len);
450+
/// unsafe {
451+
/// let slice2 = &mut *(slice as *mut [T]);
452+
/// // however, you still have two mutable references pointing to
453+
/// // the same memory.
454+
/// (&mut slice[0..mid], &mut slice2[mid..len])
455+
/// }
456+
/// }
457+
/// // This is how the standard library does it. This is the best method, if
458+
/// // you need to do something like this
459+
/// fn split_at_stdlib<T>(slice: &mut [T], mid: usize)
460+
/// -> (&mut [T], &mut [T]) {
461+
/// let len = slice.len();
462+
/// assert!(mid <= len);
463+
/// unsafe {
464+
/// let ptr = slice.as_mut_ptr();
465+
/// // This now has three mutable references pointing at the same
466+
/// // memory. `slice`, the rvalue ret.0, and the rvalue ret.1.
467+
/// // `slice` is never used after `let ptr = ...`, and so one can
468+
/// // treat it as "dead", and therefore, you only have two real
469+
/// // mutable slices.
470+
/// (slice::from_raw_parts_mut(ptr, mid),
471+
/// slice::from_raw_parts_mut(ptr.offset(mid as isize), len - mid))
472+
/// }
473+
/// }
291474
/// ```
292475
#[stable(feature = "rust1", since = "1.0.0")]
293476
pub fn transmute<T, U>(e: T) -> U;

0 commit comments

Comments
 (0)