Skip to content

Commit

Permalink
Rollup merge of rust-lang#128399 - mammothbane:master, r=Amanieu,tgro…
Browse files Browse the repository at this point in the history
…ss35

liballoc: introduce String, Vec const-slicing

This change `const`-qualifies many methods on `Vec` and `String`, notably `as_slice`, `as_str`, `len`. These changes are made behind the unstable feature flag `const_vec_string_slice`.

## Motivation
This is to support simultaneous variance over ownership and constness. I have an enum type that may contain either `String` or `&str`, and I want to produce a `&str` from it in a possibly-`const` context.

```rust
enum StrOrString<'s> {
    Str(&'s str),
    String(String),
}

impl<'s> StrOrString<'s> {
    const fn as_str(&self) -> &str {
        match self {
             // In a const-context, I really only expect to see this variant, but I can't switch the implementation
             // in some mode like #[cfg(const)] -- there has to be a single body
             Self::Str(s) => s,

             // so this is a problem, since it's not `const`
             Self::String(s) => s.as_str(),
        }
    }
}
```

Currently `String` and `Vec` don't support this, but can without functional changes. Similar logic applies for `len`, `capacity`, `is_empty`.

## Changes

The essential thing enabling this change is that `Unique::as_ptr` is `const`. This lets us convert `RawVec::ptr` -> `Vec::as_ptr` -> `Vec::as_slice` -> `String::as_str`.

I had to move the `Deref` implementations into `as_{str,slice}` because `Deref` isn't `#[const_trait]`, but I would expect this change to be invisible up to inlining. I moved the `DerefMut` implementations as well for uniformity.
  • Loading branch information
Zalathar authored Oct 7, 2024
2 parents fee7e5e + 723693e commit 6a809c7
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 30 deletions.
1 change: 1 addition & 0 deletions alloc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@
#![feature(const_option)]
#![feature(const_pin)]
#![feature(const_size_of_val)]
#![feature(const_vec_string_slice)]
#![feature(core_intrinsics)]
#![feature(deprecated_suggestion)]
#![feature(deref_pure_trait)]
Expand Down
12 changes: 6 additions & 6 deletions alloc/src/raw_vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ impl<T, A: Allocator> RawVec<T, A> {
/// `Unique::dangling()` if `capacity == 0` or `T` is zero-sized. In the former case, you must
/// be careful.
#[inline]
pub fn ptr(&self) -> *mut T {
pub const fn ptr(&self) -> *mut T {
self.inner.ptr()
}

Expand All @@ -293,7 +293,7 @@ impl<T, A: Allocator> RawVec<T, A> {
///
/// This will always be `usize::MAX` if `T` is zero-sized.
#[inline]
pub fn capacity(&self) -> usize {
pub const fn capacity(&self) -> usize {
self.inner.capacity(size_of::<T>())
}

Expand Down Expand Up @@ -488,17 +488,17 @@ impl<A: Allocator> RawVecInner<A> {
}

#[inline]
fn ptr<T>(&self) -> *mut T {
const fn ptr<T>(&self) -> *mut T {
self.non_null::<T>().as_ptr()
}

#[inline]
fn non_null<T>(&self) -> NonNull<T> {
self.ptr.cast().into()
const fn non_null<T>(&self) -> NonNull<T> {
self.ptr.cast().as_non_null_ptr()
}

#[inline]
fn capacity(&self, elem_size: usize) -> usize {
const fn capacity(&self, elem_size: usize) -> usize {
if elem_size == 0 { usize::MAX } else { self.cap.0 }
}

Expand Down
38 changes: 25 additions & 13 deletions alloc/src/string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1059,7 +1059,8 @@ impl String {
#[inline]
#[must_use = "`self` will be dropped if the result is not used"]
#[stable(feature = "rust1", since = "1.0.0")]
pub fn into_bytes(self) -> Vec<u8> {
#[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")]
pub const fn into_bytes(self) -> Vec<u8> {
self.vec
}

Expand All @@ -1076,8 +1077,11 @@ impl String {
#[must_use]
#[stable(feature = "string_as_str", since = "1.7.0")]
#[cfg_attr(not(test), rustc_diagnostic_item = "string_as_str")]
pub fn as_str(&self) -> &str {
self
#[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")]
pub const fn as_str(&self) -> &str {
// SAFETY: String contents are stipulated to be valid UTF-8, invalid contents are an error
// at construction.
unsafe { str::from_utf8_unchecked(self.vec.as_slice()) }
}

/// Converts a `String` into a mutable string slice.
Expand All @@ -1096,8 +1100,11 @@ impl String {
#[must_use]
#[stable(feature = "string_as_str", since = "1.7.0")]
#[cfg_attr(not(test), rustc_diagnostic_item = "string_as_mut_str")]
pub fn as_mut_str(&mut self) -> &mut str {
self
#[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")]
pub const fn as_mut_str(&mut self) -> &mut str {
// SAFETY: String contents are stipulated to be valid UTF-8, invalid contents are an error
// at construction.
unsafe { str::from_utf8_unchecked_mut(self.vec.as_mut_slice()) }
}

/// Appends a given string slice onto the end of this `String`.
Expand Down Expand Up @@ -1168,7 +1175,8 @@ impl String {
#[inline]
#[must_use]
#[stable(feature = "rust1", since = "1.0.0")]
pub fn capacity(&self) -> usize {
#[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")]
pub const fn capacity(&self) -> usize {
self.vec.capacity()
}

Expand Down Expand Up @@ -1431,8 +1439,9 @@ impl String {
#[inline]
#[must_use]
#[stable(feature = "rust1", since = "1.0.0")]
pub fn as_bytes(&self) -> &[u8] {
&self.vec
#[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")]
pub const fn as_bytes(&self) -> &[u8] {
self.vec.as_slice()
}

/// Shortens this `String` to the specified length.
Expand Down Expand Up @@ -1784,7 +1793,8 @@ impl String {
/// ```
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
pub unsafe fn as_mut_vec(&mut self) -> &mut Vec<u8> {
#[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")]
pub const unsafe fn as_mut_vec(&mut self) -> &mut Vec<u8> {
&mut self.vec
}

Expand All @@ -1805,8 +1815,9 @@ impl String {
#[inline]
#[must_use]
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")]
#[rustc_confusables("length", "size")]
pub fn len(&self) -> usize {
pub const fn len(&self) -> usize {
self.vec.len()
}

Expand All @@ -1824,7 +1835,8 @@ impl String {
#[inline]
#[must_use]
#[stable(feature = "rust1", since = "1.0.0")]
pub fn is_empty(&self) -> bool {
#[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")]
pub const fn is_empty(&self) -> bool {
self.len() == 0
}

Expand Down Expand Up @@ -2589,7 +2601,7 @@ impl ops::Deref for String {

#[inline]
fn deref(&self) -> &str {
unsafe { str::from_utf8_unchecked(&self.vec) }
self.as_str()
}
}

Expand All @@ -2600,7 +2612,7 @@ unsafe impl ops::DerefPure for String {}
impl ops::DerefMut for String {
#[inline]
fn deref_mut(&mut self) -> &mut str {
unsafe { str::from_utf8_unchecked_mut(&mut *self.vec) }
self.as_mut_str()
}
}

Expand Down
55 changes: 44 additions & 11 deletions alloc/src/vec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1240,7 +1240,8 @@ impl<T, A: Allocator> Vec<T, A> {
/// ```
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
pub fn capacity(&self) -> usize {
#[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")]
pub const fn capacity(&self) -> usize {
self.buf.capacity()
}

Expand Down Expand Up @@ -1548,8 +1549,22 @@ impl<T, A: Allocator> Vec<T, A> {
#[inline]
#[stable(feature = "vec_as_slice", since = "1.7.0")]
#[cfg_attr(not(test), rustc_diagnostic_item = "vec_as_slice")]
pub fn as_slice(&self) -> &[T] {
self
#[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")]
pub const fn as_slice(&self) -> &[T] {
// SAFETY: `slice::from_raw_parts` requires pointee is a contiguous, aligned buffer of size
// `len` containing properly-initialized `T`s. Data must not be mutated for the returned
// lifetime. Further, `len * mem::size_of::<T>` <= `ISIZE::MAX`, and allocation does not
// "wrap" through overflowing memory addresses.
//
// * Vec API guarantees that self.buf:
// * contains only properly-initialized items within 0..len
// * is aligned, contiguous, and valid for `len` reads
// * obeys size and address-wrapping constraints
//
// * We only construct `&mut` references to `self.buf` through `&mut self` methods; borrow-
// check ensures that it is not possible to mutably alias `self.buf` within the
// returned lifetime.
unsafe { slice::from_raw_parts(self.as_ptr(), self.len) }
}

/// Extracts a mutable slice of the entire vector.
Expand All @@ -1566,8 +1581,22 @@ impl<T, A: Allocator> Vec<T, A> {
#[inline]
#[stable(feature = "vec_as_slice", since = "1.7.0")]
#[cfg_attr(not(test), rustc_diagnostic_item = "vec_as_mut_slice")]
pub fn as_mut_slice(&mut self) -> &mut [T] {
self
#[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")]
pub const fn as_mut_slice(&mut self) -> &mut [T] {
// SAFETY: `slice::from_raw_parts_mut` requires pointee is a contiguous, aligned buffer of
// size `len` containing properly-initialized `T`s. Data must not be accessed through any
// other pointer for the returned lifetime. Further, `len * mem::size_of::<T>` <=
// `ISIZE::MAX` and allocation does not "wrap" through overflowing memory addresses.
//
// * Vec API guarantees that self.buf:
// * contains only properly-initialized items within 0..len
// * is aligned, contiguous, and valid for `len` reads
// * obeys size and address-wrapping constraints
//
// * We only construct references to `self.buf` through `&self` and `&mut self` methods;
// borrow-check ensures that it is not possible to construct a reference to `self.buf`
// within the returned lifetime.
unsafe { slice::from_raw_parts_mut(self.as_mut_ptr(), self.len) }
}

/// Returns a raw pointer to the vector's buffer, or a dangling raw pointer
Expand Down Expand Up @@ -1624,9 +1653,10 @@ impl<T, A: Allocator> Vec<T, A> {
/// [`as_ptr`]: Vec::as_ptr
/// [`as_non_null`]: Vec::as_non_null
#[stable(feature = "vec_as_ptr", since = "1.37.0")]
#[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")]
#[rustc_never_returns_null_ptr]
#[inline]
pub fn as_ptr(&self) -> *const T {
pub const fn as_ptr(&self) -> *const T {
// We shadow the slice method of the same name to avoid going through
// `deref`, which creates an intermediate reference.
self.buf.ptr()
Expand Down Expand Up @@ -1685,9 +1715,10 @@ impl<T, A: Allocator> Vec<T, A> {
/// [`as_ptr`]: Vec::as_ptr
/// [`as_non_null`]: Vec::as_non_null
#[stable(feature = "vec_as_ptr", since = "1.37.0")]
#[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")]
#[rustc_never_returns_null_ptr]
#[inline]
pub fn as_mut_ptr(&mut self) -> *mut T {
pub const fn as_mut_ptr(&mut self) -> *mut T {
// We shadow the slice method of the same name to avoid going through
// `deref_mut`, which creates an intermediate reference.
self.buf.ptr()
Expand Down Expand Up @@ -2628,8 +2659,9 @@ impl<T, A: Allocator> Vec<T, A> {
/// ```
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")]
#[rustc_confusables("length", "size")]
pub fn len(&self) -> usize {
pub const fn len(&self) -> usize {
self.len
}

Expand All @@ -2646,7 +2678,8 @@ impl<T, A: Allocator> Vec<T, A> {
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[cfg_attr(not(test), rustc_diagnostic_item = "vec_is_empty")]
pub fn is_empty(&self) -> bool {
#[rustc_const_unstable(feature = "const_vec_string_slice", issue = "129041")]
pub const fn is_empty(&self) -> bool {
self.len() == 0
}

Expand Down Expand Up @@ -3197,15 +3230,15 @@ impl<T, A: Allocator> ops::Deref for Vec<T, A> {

#[inline]
fn deref(&self) -> &[T] {
unsafe { slice::from_raw_parts(self.as_ptr(), self.len) }
self.as_slice()
}
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<T, A: Allocator> ops::DerefMut for Vec<T, A> {
#[inline]
fn deref_mut(&mut self) -> &mut [T] {
unsafe { slice::from_raw_parts_mut(self.as_mut_ptr(), self.len) }
self.as_mut_slice()
}
}

Expand Down

0 comments on commit 6a809c7

Please sign in to comment.