Skip to content

Commit 7a9eb83

Browse files
Rollup merge of rust-lang#49767 - ecstatic-morse:ptr-docs, r=steveklabnik
Rewrite docs for `std::ptr` This PR attempts to resolve rust-lang#29371. This is a fairly major rewrite of the `std::ptr` docs, and deserves a fair bit of scrutiny. It adds links to the GNU libc docs for various instrinsics, adds internal links to types and functions referenced in the docs, adds new, more complex examples for many functions, and introduces a common template for discussing unsafety of functions in `std::ptr`. All functions in `std::ptr` (with the exception of `ptr::eq`) are unsafe because they either read from or write to a raw pointer. The "Safety" section now informs that the function is unsafe because it dereferences a raw pointer and requires that any pointer to be read by the function points to "a valid vaue of type `T`". Additionally, each function imposes some subset of the following conditions on its arguments. * The pointer points to valid memory. * The pointer points to initialized memory. * The pointer is properly aligned. These requirements are discussed in the "Undefined Behavior" section along with the consequences of using functions that perform bitwise copies without requiring `T: Copy`. I don't love my new descriptions of the consequences of making such copies. Perhaps the old ones were good enough? Some issues which still need to be addressed before this is merged: - [ ] The new docs assert that `drop_in_place` is equivalent to calling `read` and discarding the value. Is this correct? - [ ] Do `write_bytes` and `swap_nonoverlapping` require properly aligned pointers? - [ ] The new example for `drop_in_place` is a lackluster. - [ ] Should these docs rigorously define what `valid` memory is? Or should is that the job of the reference? Should we link to the reference? - [ ] Is it correct to require that pointers that will be read from refer to "valid values of type `T`"? - [x] I can't imagine ever using `{read,write}_volatile` with non-`Copy` types. Should I just link to {read,write} and say that the same issues with non-`Copy` types apply? - [x] `write_volatile` doesn't link back to `read_volatile`. - [ ] Update docs for the unstable [`swap_nonoverlapping`](rust-lang#42818) - [ ] Update docs for the unstable [unsafe pointer methods RFC](rust-lang/rfcs#1966) Looking forward to your feedback. r? @steveklabnik
2 parents 0b17da2 + 827251e commit 7a9eb83

File tree

2 files changed

+430
-101
lines changed

2 files changed

+430
-101
lines changed

src/libcore/intrinsics.rs

+130-31
Original file line numberDiff line numberDiff line change
@@ -962,59 +962,122 @@ extern "rust-intrinsic" {
962962
/// value is not necessarily valid to be used to actually access memory.
963963
pub fn arith_offset<T>(dst: *const T, offset: isize) -> *const T;
964964

965-
/// Copies `count * size_of<T>` bytes from `src` to `dst`. The source
966-
/// and destination may *not* overlap.
965+
/// Copies `count * size_of::<T>()` bytes from `src` to `dst`. The source
966+
/// and destination must *not* overlap.
967967
///
968-
/// `copy_nonoverlapping` is semantically equivalent to C's `memcpy`.
968+
/// For regions of memory which might overlap, use [`copy`] instead.
969+
///
970+
/// `copy_nonoverlapping` is semantically equivalent to C's [`memcpy`].
971+
///
972+
/// [`copy`]: ./fn.copy.html
973+
/// [`memcpy`]: https://www.gnu.org/software/libc/manual/html_node/Copying-Strings-and-Arrays.html#index-memcpy
969974
///
970975
/// # Safety
971976
///
972-
/// Beyond requiring that the program must be allowed to access both regions
973-
/// of memory, it is Undefined Behavior for source and destination to
974-
/// overlap. Care must also be taken with the ownership of `src` and
975-
/// `dst`. This method semantically moves the values of `src` into `dst`.
976-
/// However it does not drop the contents of `dst`, or prevent the contents
977-
/// of `src` from being dropped or used.
977+
/// Behavior is undefined if any of the following conditions are violated:
978+
///
979+
/// * The region of memory which begins at `src` and has a length of
980+
/// `count * size_of::<T>()` bytes must be *both* valid and initialized.
981+
///
982+
/// * The region of memory which begins at `dst` and has a length of
983+
/// `count * size_of::<T>()` bytes must be valid (but may or may not be
984+
/// initialized).
985+
///
986+
/// * The two regions of memory must *not* overlap.
987+
///
988+
/// * `src` must be properly aligned.
989+
///
990+
/// * `dst` must be properly aligned.
991+
///
992+
/// Additionally, if `T` is not [`Copy`], only the region at `src` *or* the
993+
/// region at `dst` can be used or dropped after calling
994+
/// `copy_nonoverlapping`. `copy_nonoverlapping` creates bitwise copies of
995+
/// `T`, regardless of whether `T: Copy`, which can result in undefined
996+
/// behavior if both copies are used.
997+
///
998+
/// [`Copy`]: ../marker/trait.Copy.html
978999
///
9791000
/// # Examples
9801001
///
981-
/// A safe swap function:
1002+
/// Manually implement [`Vec::append`]:
9821003
///
9831004
/// ```
984-
/// use std::mem;
9851005
/// use std::ptr;
9861006
///
987-
/// # #[allow(dead_code)]
988-
/// fn swap<T>(x: &mut T, y: &mut T) {
1007+
/// /// Moves all the elements of `src` into `dst`, leaving `src` empty.
1008+
/// fn append<T>(dst: &mut Vec<T>, src: &mut Vec<T>) {
1009+
/// let src_len = src.len();
1010+
/// let dst_len = dst.len();
1011+
///
1012+
/// // Ensure that `dst` has enough capacity to hold all of `src`.
1013+
/// dst.reserve(src_len);
1014+
///
9891015
/// unsafe {
990-
/// // Give ourselves some scratch space to work with
991-
/// let mut t: T = mem::uninitialized();
1016+
/// // The call to offset is always safe because `Vec` will never
1017+
/// // allocate more than `isize::MAX` bytes.
1018+
/// let dst = dst.as_mut_ptr().offset(dst_len as isize);
1019+
/// let src = src.as_ptr();
1020+
///
1021+
/// // The two regions cannot overlap becuase mutable references do
1022+
/// // not alias, and two different vectors cannot own the same
1023+
/// // memory.
1024+
/// ptr::copy_nonoverlapping(src, dst, src_len);
1025+
/// }
9921026
///
993-
/// // Perform the swap, `&mut` pointers never alias
994-
/// ptr::copy_nonoverlapping(x, &mut t, 1);
995-
/// ptr::copy_nonoverlapping(y, x, 1);
996-
/// ptr::copy_nonoverlapping(&t, y, 1);
1027+
/// unsafe {
1028+
/// // Truncate `src` without dropping its contents.
1029+
/// src.set_len(0);
9971030
///
998-
/// // y and t now point to the same thing, but we need to completely forget `t`
999-
/// // because it's no longer relevant.
1000-
/// mem::forget(t);
1031+
/// // Notify `dst` that it now holds the contents of `src`.
1032+
/// dst.set_len(dst_len + src_len);
10011033
/// }
10021034
/// }
1035+
///
1036+
/// let mut a = vec!['r'];
1037+
/// let mut b = vec!['u', 's', 't'];
1038+
///
1039+
/// append(&mut a, &mut b);
1040+
///
1041+
/// assert_eq!(a, &['r', 'u', 's', 't']);
1042+
/// assert!(b.is_empty());
10031043
/// ```
1044+
///
1045+
/// [`Vec::append`]: ../../std/vec/struct.Vec.html#method.append
10041046
#[stable(feature = "rust1", since = "1.0.0")]
10051047
pub fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: usize);
10061048

1007-
/// Copies `count * size_of<T>` bytes from `src` to `dst`. The source
1049+
/// Copies `count * size_of::<T>()` bytes from `src` to `dst`. The source
10081050
/// and destination may overlap.
10091051
///
1010-
/// `copy` is semantically equivalent to C's `memmove`.
1052+
/// If the source and destination will *never* overlap,
1053+
/// [`copy_nonoverlapping`] can be used instead.
1054+
///
1055+
/// `copy` is semantically equivalent to C's [`memmove`].
1056+
///
1057+
/// [`copy_nonoverlapping`]: ./fn.copy_nonoverlapping.html
1058+
/// [`memmove`]: https://www.gnu.org/software/libc/manual/html_node/Copying-Strings-and-Arrays.html#index-memmove
10111059
///
10121060
/// # Safety
10131061
///
1014-
/// Care must be taken with the ownership of `src` and `dst`.
1015-
/// This method semantically moves the values of `src` into `dst`.
1016-
/// However it does not drop the contents of `dst`, or prevent the contents of `src`
1017-
/// from being dropped or used.
1062+
/// Behavior is undefined if any of the following conditions are violated:
1063+
///
1064+
/// * The region of memory which begins at `src` and has a length of
1065+
/// `count * size_of::<T>()` bytes must be *both* valid and initialized.
1066+
///
1067+
/// * The region of memory which begins at `dst` and has a length of
1068+
/// `count * size_of::<T>()` bytes must be valid (but may or may not be
1069+
/// initialized).
1070+
///
1071+
/// * `src` must be properly aligned.
1072+
///
1073+
/// * `dst` must be properly aligned.
1074+
///
1075+
/// Additionally, if `T` is not [`Copy`], only the region at `src` *or* the
1076+
/// region at `dst` can be used or dropped after calling `copy`. `copy`
1077+
/// creates bitwise copies of `T`, regardless of whether `T: Copy`, which
1078+
/// can result in undefined behavior if both copies are used.
1079+
///
1080+
/// [`Copy`]: ../marker/trait.Copy.html
10181081
///
10191082
/// # Examples
10201083
///
@@ -1031,15 +1094,34 @@ extern "rust-intrinsic" {
10311094
/// dst
10321095
/// }
10331096
/// ```
1034-
///
10351097
#[stable(feature = "rust1", since = "1.0.0")]
10361098
pub fn copy<T>(src: *const T, dst: *mut T, count: usize);
10371099

1038-
/// Invokes memset on the specified pointer, setting `count * size_of::<T>()`
1039-
/// bytes of memory starting at `dst` to `val`.
1100+
/// Sets `count * size_of::<T>()` bytes of memory starting at `dst` to
1101+
/// `val`.
1102+
///
1103+
/// `write_bytes` is semantically equivalent to C's [`memset`].
1104+
///
1105+
/// [`memset`]: https://www.gnu.org/software/libc/manual/html_node/Copying-Strings-and-Arrays.html#index-memset
1106+
///
1107+
/// # Safety
1108+
///
1109+
/// Behavior is undefined if any of the following conditions are violated:
1110+
///
1111+
/// * The region of memory which begins at `dst` and has a length of
1112+
/// `count` bytes must be valid.
1113+
///
1114+
/// * `dst` must be properly aligned.
1115+
///
1116+
/// Additionally, the caller must ensure that writing `count` bytes to the
1117+
/// given region of memory results in a valid value of `T`. Creating an
1118+
/// invalid value of `T` can result in undefined behavior. An example is
1119+
/// provided below.
10401120
///
10411121
/// # Examples
10421122
///
1123+
/// Basic usage:
1124+
///
10431125
/// ```
10441126
/// use std::ptr;
10451127
///
@@ -1050,6 +1132,23 @@ extern "rust-intrinsic" {
10501132
/// }
10511133
/// assert_eq!(vec, [b'a', b'a', 0, 0]);
10521134
/// ```
1135+
///
1136+
/// Creating an invalid value:
1137+
///
1138+
/// ```no_run
1139+
/// use std::{mem, ptr};
1140+
///
1141+
/// let mut v = Box::new(0i32);
1142+
///
1143+
/// unsafe {
1144+
/// // Leaks the previously held value by overwriting the `Box<T>` with
1145+
/// // a null pointer.
1146+
/// ptr::write_bytes(&mut v, 0, mem::size_of::<Box<i32>>());
1147+
/// }
1148+
///
1149+
/// // At this point, using or dropping `v` results in undefined behavior.
1150+
/// // v = Box::new(0i32); // ERROR
1151+
/// ```
10531152
#[stable(feature = "rust1", since = "1.0.0")]
10541153
pub fn write_bytes<T>(dst: *mut T, val: u8, count: usize);
10551154

0 commit comments

Comments
 (0)