Skip to content

Commit 459f246

Browse files
committed
Add NonNull convenience methods to Vec
1 parent a2e7f8b commit 459f246

File tree

3 files changed

+308
-16
lines changed

3 files changed

+308
-16
lines changed

alloc/src/vec/in_place_collect.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,7 @@ where
328328

329329
mem::forget(dst_guard);
330330

331-
let vec = unsafe { Vec::from_nonnull(dst_buf, len, dst_cap) };
331+
let vec = unsafe { Vec::from_parts(dst_buf, len, dst_cap) };
332332

333333
vec
334334
}

alloc/src/vec/mod.rs

+306-14
Original file line numberDiff line numberDiff line change
@@ -603,15 +603,116 @@ impl<T> Vec<T> {
603603
unsafe { Self::from_raw_parts_in(ptr, length, capacity, Global) }
604604
}
605605

606-
/// A convenience method for hoisting the non-null precondition out of [`Vec::from_raw_parts`].
606+
#[doc(alias = "from_non_null_parts")]
607+
/// Creates a `Vec<T>` directly from a `NonNull` pointer, a length, and a capacity.
607608
///
608609
/// # Safety
609610
///
610-
/// See [`Vec::from_raw_parts`].
611+
/// This is highly unsafe, due to the number of invariants that aren't
612+
/// checked:
613+
///
614+
/// * `ptr` must have been allocated using the global allocator, such as via
615+
/// the [`alloc::alloc`] function.
616+
/// * `T` needs to have the same alignment as what `ptr` was allocated with.
617+
/// (`T` having a less strict alignment is not sufficient, the alignment really
618+
/// needs to be equal to satisfy the [`dealloc`] requirement that memory must be
619+
/// allocated and deallocated with the same layout.)
620+
/// * The size of `T` times the `capacity` (ie. the allocated size in bytes) needs
621+
/// to be the same size as the pointer was allocated with. (Because similar to
622+
/// alignment, [`dealloc`] must be called with the same layout `size`.)
623+
/// * `length` needs to be less than or equal to `capacity`.
624+
/// * The first `length` values must be properly initialized values of type `T`.
625+
/// * `capacity` needs to be the capacity that the pointer was allocated with.
626+
/// * The allocated size in bytes must be no larger than `isize::MAX`.
627+
/// See the safety documentation of [`pointer::offset`].
628+
///
629+
/// These requirements are always upheld by any `ptr` that has been allocated
630+
/// via `Vec<T>`. Other allocation sources are allowed if the invariants are
631+
/// upheld.
632+
///
633+
/// Violating these may cause problems like corrupting the allocator's
634+
/// internal data structures. For example it is normally **not** safe
635+
/// to build a `Vec<u8>` from a pointer to a C `char` array with length
636+
/// `size_t`, doing so is only safe if the array was initially allocated by
637+
/// a `Vec` or `String`.
638+
/// It's also not safe to build one from a `Vec<u16>` and its length, because
639+
/// the allocator cares about the alignment, and these two types have different
640+
/// alignments. The buffer was allocated with alignment 2 (for `u16`), but after
641+
/// turning it into a `Vec<u8>` it'll be deallocated with alignment 1. To avoid
642+
/// these issues, it is often preferable to do casting/transmuting using
643+
/// [`NonNull::slice_from_raw_parts`] instead.
644+
///
645+
/// The ownership of `ptr` is effectively transferred to the
646+
/// `Vec<T>` which may then deallocate, reallocate or change the
647+
/// contents of memory pointed to by the pointer at will. Ensure
648+
/// that nothing else uses the pointer after calling this
649+
/// function.
650+
///
651+
/// [`String`]: crate::string::String
652+
/// [`alloc::alloc`]: crate::alloc::alloc
653+
/// [`dealloc`]: crate::alloc::GlobalAlloc::dealloc
654+
///
655+
/// # Examples
656+
///
657+
/// ```
658+
/// #![feature(box_vec_non_null)]
659+
///
660+
/// use std::ptr::NonNull;
661+
/// use std::mem;
662+
///
663+
/// let v = vec![1, 2, 3];
664+
///
665+
// FIXME Update this when vec_into_raw_parts is stabilized
666+
/// // Prevent running `v`'s destructor so we are in complete control
667+
/// // of the allocation.
668+
/// let mut v = mem::ManuallyDrop::new(v);
669+
///
670+
/// // Pull out the various important pieces of information about `v`
671+
/// let p = unsafe { NonNull::new_unchecked(v.as_mut_ptr()) };
672+
/// let len = v.len();
673+
/// let cap = v.capacity();
674+
///
675+
/// unsafe {
676+
/// // Overwrite memory with 4, 5, 6
677+
/// for i in 0..len {
678+
/// p.add(i).write(4 + i);
679+
/// }
680+
///
681+
/// // Put everything back together into a Vec
682+
/// let rebuilt = Vec::from_parts(p, len, cap);
683+
/// assert_eq!(rebuilt, [4, 5, 6]);
684+
/// }
685+
/// ```
686+
///
687+
/// Using memory that was allocated elsewhere:
688+
///
689+
/// ```rust
690+
/// #![feature(box_vec_non_null)]
691+
///
692+
/// use std::alloc::{alloc, Layout};
693+
/// use std::ptr::NonNull;
694+
///
695+
/// fn main() {
696+
/// let layout = Layout::array::<u32>(16).expect("overflow cannot happen");
697+
///
698+
/// let vec = unsafe {
699+
/// let Some(mem) = NonNull::new(alloc(layout).cast::<u32>()) else {
700+
/// return;
701+
/// };
702+
///
703+
/// mem.write(1_000_000);
704+
///
705+
/// Vec::from_parts(mem, 1, 16)
706+
/// };
707+
///
708+
/// assert_eq!(vec, &[1_000_000]);
709+
/// assert_eq!(vec.capacity(), 16);
710+
/// }
711+
/// ```
611712
#[inline]
612-
#[cfg(not(no_global_oom_handling))] // required by tests/run-make/alloc-no-oom-handling
613-
pub(crate) unsafe fn from_nonnull(ptr: NonNull<T>, length: usize, capacity: usize) -> Self {
614-
unsafe { Self::from_nonnull_in(ptr, length, capacity, Global) }
713+
#[unstable(feature = "box_vec_non_null", reason = "new API", issue = "none")]
714+
pub unsafe fn from_parts(ptr: NonNull<T>, length: usize, capacity: usize) -> Self {
715+
unsafe { Self::from_parts_in(ptr, length, capacity, Global) }
615716
}
616717
}
617718

@@ -830,19 +931,119 @@ impl<T, A: Allocator> Vec<T, A> {
830931
unsafe { Vec { buf: RawVec::from_raw_parts_in(ptr, capacity, alloc), len: length } }
831932
}
832933

833-
/// A convenience method for hoisting the non-null precondition out of [`Vec::from_raw_parts_in`].
934+
#[doc(alias = "from_non_null_parts_in")]
935+
/// Creates a `Vec<T, A>` directly from a `NonNull` pointer, a length, a capacity,
936+
/// and an allocator.
834937
///
835938
/// # Safety
836939
///
837-
/// See [`Vec::from_raw_parts_in`].
940+
/// This is highly unsafe, due to the number of invariants that aren't
941+
/// checked:
942+
///
943+
/// * `ptr` must be [*currently allocated*] via the given allocator `alloc`.
944+
/// * `T` needs to have the same alignment as what `ptr` was allocated with.
945+
/// (`T` having a less strict alignment is not sufficient, the alignment really
946+
/// needs to be equal to satisfy the [`dealloc`] requirement that memory must be
947+
/// allocated and deallocated with the same layout.)
948+
/// * The size of `T` times the `capacity` (ie. the allocated size in bytes) needs
949+
/// to be the same size as the pointer was allocated with. (Because similar to
950+
/// alignment, [`dealloc`] must be called with the same layout `size`.)
951+
/// * `length` needs to be less than or equal to `capacity`.
952+
/// * The first `length` values must be properly initialized values of type `T`.
953+
/// * `capacity` needs to [*fit*] the layout size that the pointer was allocated with.
954+
/// * The allocated size in bytes must be no larger than `isize::MAX`.
955+
/// See the safety documentation of [`pointer::offset`].
956+
///
957+
/// These requirements are always upheld by any `ptr` that has been allocated
958+
/// via `Vec<T, A>`. Other allocation sources are allowed if the invariants are
959+
/// upheld.
960+
///
961+
/// Violating these may cause problems like corrupting the allocator's
962+
/// internal data structures. For example it is **not** safe
963+
/// to build a `Vec<u8>` from a pointer to a C `char` array with length `size_t`.
964+
/// It's also not safe to build one from a `Vec<u16>` and its length, because
965+
/// the allocator cares about the alignment, and these two types have different
966+
/// alignments. The buffer was allocated with alignment 2 (for `u16`), but after
967+
/// turning it into a `Vec<u8>` it'll be deallocated with alignment 1.
968+
///
969+
/// The ownership of `ptr` is effectively transferred to the
970+
/// `Vec<T>` which may then deallocate, reallocate or change the
971+
/// contents of memory pointed to by the pointer at will. Ensure
972+
/// that nothing else uses the pointer after calling this
973+
/// function.
974+
///
975+
/// [`String`]: crate::string::String
976+
/// [`dealloc`]: crate::alloc::GlobalAlloc::dealloc
977+
/// [*currently allocated*]: crate::alloc::Allocator#currently-allocated-memory
978+
/// [*fit*]: crate::alloc::Allocator#memory-fitting
979+
///
980+
/// # Examples
981+
///
982+
/// ```
983+
/// #![feature(allocator_api, box_vec_non_null)]
984+
///
985+
/// use std::alloc::System;
986+
///
987+
/// use std::ptr::NonNull;
988+
/// use std::mem;
989+
///
990+
/// let mut v = Vec::with_capacity_in(3, System);
991+
/// v.push(1);
992+
/// v.push(2);
993+
/// v.push(3);
994+
///
995+
// FIXME Update this when vec_into_raw_parts is stabilized
996+
/// // Prevent running `v`'s destructor so we are in complete control
997+
/// // of the allocation.
998+
/// let mut v = mem::ManuallyDrop::new(v);
999+
///
1000+
/// // Pull out the various important pieces of information about `v`
1001+
/// let p = unsafe { NonNull::new_unchecked(v.as_mut_ptr()) };
1002+
/// let len = v.len();
1003+
/// let cap = v.capacity();
1004+
/// let alloc = v.allocator();
1005+
///
1006+
/// unsafe {
1007+
/// // Overwrite memory with 4, 5, 6
1008+
/// for i in 0..len {
1009+
/// p.add(i).write(4 + i);
1010+
/// }
1011+
///
1012+
/// // Put everything back together into a Vec
1013+
/// let rebuilt = Vec::from_parts_in(p, len, cap, alloc.clone());
1014+
/// assert_eq!(rebuilt, [4, 5, 6]);
1015+
/// }
1016+
/// ```
1017+
///
1018+
/// Using memory that was allocated elsewhere:
1019+
///
1020+
/// ```rust
1021+
/// #![feature(allocator_api, box_vec_non_null)]
1022+
///
1023+
/// use std::alloc::{AllocError, Allocator, Global, Layout};
1024+
///
1025+
/// fn main() {
1026+
/// let layout = Layout::array::<u32>(16).expect("overflow cannot happen");
1027+
///
1028+
/// let vec = unsafe {
1029+
/// let mem = match Global.allocate(layout) {
1030+
/// Ok(mem) => mem.cast::<u32>(),
1031+
/// Err(AllocError) => return,
1032+
/// };
1033+
///
1034+
/// mem.write(1_000_000);
1035+
///
1036+
/// Vec::from_parts_in(mem, 1, 16, Global)
1037+
/// };
1038+
///
1039+
/// assert_eq!(vec, &[1_000_000]);
1040+
/// assert_eq!(vec.capacity(), 16);
1041+
/// }
1042+
/// ```
8381043
#[inline]
839-
#[cfg(not(no_global_oom_handling))] // required by tests/run-make/alloc-no-oom-handling
840-
pub(crate) unsafe fn from_nonnull_in(
841-
ptr: NonNull<T>,
842-
length: usize,
843-
capacity: usize,
844-
alloc: A,
845-
) -> Self {
1044+
#[unstable(feature = "allocator_api", reason = "new API", issue = "32838")]
1045+
// #[unstable(feature = "box_vec_non_null", issue = "none")]
1046+
pub unsafe fn from_parts_in(ptr: NonNull<T>, length: usize, capacity: usize, alloc: A) -> Self {
8461047
unsafe { Vec { buf: RawVec::from_nonnull_in(ptr, capacity, alloc), len: length } }
8471048
}
8481049

@@ -885,6 +1086,49 @@ impl<T, A: Allocator> Vec<T, A> {
8851086
(me.as_mut_ptr(), me.len(), me.capacity())
8861087
}
8871088

1089+
#[doc(alias = "into_non_null_parts")]
1090+
/// Decomposes a `Vec<T>` into its raw components: `(NonNull pointer, length, capacity)`.
1091+
///
1092+
/// Returns the `NonNull` pointer to the underlying data, the length of
1093+
/// the vector (in elements), and the allocated capacity of the
1094+
/// data (in elements). These are the same arguments in the same
1095+
/// order as the arguments to [`from_parts`].
1096+
///
1097+
/// After calling this function, the caller is responsible for the
1098+
/// memory previously managed by the `Vec`. The only way to do
1099+
/// this is to convert the `NonNull` pointer, length, and capacity back
1100+
/// into a `Vec` with the [`from_parts`] function, allowing
1101+
/// the destructor to perform the cleanup.
1102+
///
1103+
/// [`from_parts`]: Vec::from_parts
1104+
///
1105+
/// # Examples
1106+
///
1107+
/// ```
1108+
/// #![feature(vec_into_raw_parts, box_vec_non_null)]
1109+
///
1110+
/// let v: Vec<i32> = vec![-1, 0, 1];
1111+
///
1112+
/// let (ptr, len, cap) = v.into_parts();
1113+
///
1114+
/// let rebuilt = unsafe {
1115+
/// // We can now make changes to the components, such as
1116+
/// // transmuting the raw pointer to a compatible type.
1117+
/// let ptr = ptr.cast::<u32>();
1118+
///
1119+
/// Vec::from_parts(ptr, len, cap)
1120+
/// };
1121+
/// assert_eq!(rebuilt, [4294967295, 0, 1]);
1122+
/// ```
1123+
#[must_use = "losing the pointer will leak memory"]
1124+
#[unstable(feature = "box_vec_non_null", reason = "new API", issue = "none")]
1125+
// #[unstable(feature = "vec_into_raw_parts", reason = "new API", issue = "65816")]
1126+
pub fn into_parts(self) -> (NonNull<T>, usize, usize) {
1127+
let (ptr, len, capacity) = self.into_raw_parts();
1128+
// SAFETY: A `Vec` always has a non-null pointer.
1129+
(unsafe { NonNull::new_unchecked(ptr) }, len, capacity)
1130+
}
1131+
8881132
/// Decomposes a `Vec<T>` into its raw components: `(pointer, length, capacity, allocator)`.
8891133
///
8901134
/// Returns the raw pointer to the underlying data, the length of the vector (in elements),
@@ -934,6 +1178,54 @@ impl<T, A: Allocator> Vec<T, A> {
9341178
(ptr, len, capacity, alloc)
9351179
}
9361180

1181+
#[doc(alias = "into_non_null_parts_with_alloc")]
1182+
/// Decomposes a `Vec<T>` into its raw components: `(NonNull pointer, length, capacity, allocator)`.
1183+
///
1184+
/// Returns the `NonNull` pointer to the underlying data, the length of the vector (in elements),
1185+
/// the allocated capacity of the data (in elements), and the allocator. These are the same
1186+
/// arguments in the same order as the arguments to [`from_parts_in`].
1187+
///
1188+
/// After calling this function, the caller is responsible for the
1189+
/// memory previously managed by the `Vec`. The only way to do
1190+
/// this is to convert the `NonNull` pointer, length, and capacity back
1191+
/// into a `Vec` with the [`from_parts_in`] function, allowing
1192+
/// the destructor to perform the cleanup.
1193+
///
1194+
/// [`from_parts_in`]: Vec::from_parts_in
1195+
///
1196+
/// # Examples
1197+
///
1198+
/// ```
1199+
/// #![feature(allocator_api, vec_into_raw_parts, box_vec_non_null)]
1200+
///
1201+
/// use std::alloc::System;
1202+
///
1203+
/// let mut v: Vec<i32, System> = Vec::new_in(System);
1204+
/// v.push(-1);
1205+
/// v.push(0);
1206+
/// v.push(1);
1207+
///
1208+
/// let (ptr, len, cap, alloc) = v.into_parts_with_alloc();
1209+
///
1210+
/// let rebuilt = unsafe {
1211+
/// // We can now make changes to the components, such as
1212+
/// // transmuting the raw pointer to a compatible type.
1213+
/// let ptr = ptr.cast::<u32>();
1214+
///
1215+
/// Vec::from_parts_in(ptr, len, cap, alloc)
1216+
/// };
1217+
/// assert_eq!(rebuilt, [4294967295, 0, 1]);
1218+
/// ```
1219+
#[must_use = "losing the pointer will leak memory"]
1220+
#[unstable(feature = "allocator_api", issue = "32838")]
1221+
// #[unstable(feature = "box_vec_non_null", reason = "new API", issue = "none")]
1222+
// #[unstable(feature = "vec_into_raw_parts", reason = "new API", issue = "65816")]
1223+
pub fn into_parts_with_alloc(self) -> (NonNull<T>, usize, usize, A) {
1224+
let (ptr, len, capacity, alloc) = self.into_raw_parts_with_alloc();
1225+
// SAFETY: A `Vec` always has a non-null pointer.
1226+
(unsafe { NonNull::new_unchecked(ptr) }, len, capacity, alloc)
1227+
}
1228+
9371229
/// Returns the total number of elements the vector can hold without
9381230
/// reallocating.
9391231
///

alloc/src/vec/spec_from_iter.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ impl<T> SpecFromIter<T, IntoIter<T>> for Vec<T> {
5151
if has_advanced {
5252
ptr::copy(it.ptr.as_ptr(), it.buf.as_ptr(), it.len());
5353
}
54-
return Vec::from_nonnull(it.buf, it.len(), it.cap);
54+
return Vec::from_parts(it.buf, it.len(), it.cap);
5555
}
5656
}
5757

0 commit comments

Comments
 (0)