Skip to content

Commit

Permalink
Rollup merge of #107624 - tgross35:const-cstr-methods, r=dtolnay
Browse files Browse the repository at this point in the history
Stabilize `const_cstr_methods`

This PR seeks to stabilize `const_cstr_methods`. Fixes most of #101719

## New const stable API

```rust
impl CStr {
    // depends: memchr
    pub const fn from_bytes_with_nul(bytes: &[u8]) -> Result<&Self, FromBytesWithNulError> {...}
    // depends: const_slice_index
    pub const fn to_bytes(&self) -> &[u8] {}
    // depends: pointer casts
    pub const fn to_bytes_with_nul(&self) -> &[u8] {}
    // depends: str::from_utf8
    pub const fn to_str(&self) -> Result<&str, str::Utf8Error> {}
}
```

I don't think any of these methods will have any issue when `CStr` becomes a thin pointer as long as `memchr` is const  (which also allows for const `strlen`) .

## Notes

- `from_bytes_until_nul` relies on `const_slice_index`, which relies on `const_trait_impls`, and generally this should be avoided. After talking with Oli, it should be OK in this case because we could replace the ranges with pointer tricks if needed (worst case being those feature gates disappear). #107624 (comment)
- Making `from_ptr` const is deferred because it depends on `const_eval_select`. I have moved this under the new flag `const_cstr_from_ptr` #107624 (comment)

cc ``@oli-obk`` I think you're the const expert

``@rustbot`` modify labels: +T-libs-api +needs-fcp
  • Loading branch information
matthiaskrgr authored Jun 30, 2023
2 parents 97279e9 + 5cb701f commit 016c306
Show file tree
Hide file tree
Showing 2 changed files with 10 additions and 8 deletions.
17 changes: 10 additions & 7 deletions library/core/src/ffi/c_str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ impl CStr {
/// ```
///
/// ```
/// #![feature(const_cstr_methods)]
/// #![feature(const_cstr_from_ptr)]
///
/// use std::ffi::{c_char, CStr};
///
Expand All @@ -256,7 +256,7 @@ impl CStr {
#[inline]
#[must_use]
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature = "const_cstr_methods", issue = "101719")]
#[rustc_const_unstable(feature = "const_cstr_from_ptr", issue = "101719")]
pub const unsafe fn from_ptr<'a>(ptr: *const c_char) -> &'a CStr {
// SAFETY: The caller has provided a pointer that points to a valid C
// string with a NUL terminator of size less than `isize::MAX`, whose
Expand Down Expand Up @@ -377,7 +377,7 @@ impl CStr {
/// assert!(cstr.is_err());
/// ```
#[stable(feature = "cstr_from_bytes", since = "1.10.0")]
#[rustc_const_unstable(feature = "const_cstr_methods", issue = "101719")]
#[rustc_const_stable(feature = "const_cstr_methods", since = "CURRENT_RUSTC_VERSION")]
pub const fn from_bytes_with_nul(bytes: &[u8]) -> Result<&Self, FromBytesWithNulError> {
let nul_pos = memchr::memchr(0, bytes);
match nul_pos {
Expand Down Expand Up @@ -561,10 +561,12 @@ impl CStr {
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[stable(feature = "rust1", since = "1.0.0")]
pub fn to_bytes(&self) -> &[u8] {
#[rustc_const_stable(feature = "const_cstr_methods", since = "CURRENT_RUSTC_VERSION")]
pub const fn to_bytes(&self) -> &[u8] {
let bytes = self.to_bytes_with_nul();
// FIXME(const-hack) replace with range index
// SAFETY: to_bytes_with_nul returns slice with length at least 1
unsafe { bytes.get_unchecked(..bytes.len() - 1) }
unsafe { slice::from_raw_parts(bytes.as_ptr(), bytes.len() - 1) }
}

/// Converts this C string to a byte slice containing the trailing 0 byte.
Expand All @@ -588,7 +590,7 @@ impl CStr {
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature = "const_cstr_methods", issue = "101719")]
#[rustc_const_stable(feature = "const_cstr_methods", since = "CURRENT_RUSTC_VERSION")]
pub const fn to_bytes_with_nul(&self) -> &[u8] {
// SAFETY: Transmuting a slice of `c_char`s to a slice of `u8`s
// is safe on all supported targets.
Expand All @@ -612,7 +614,8 @@ impl CStr {
/// assert_eq!(cstr.to_str(), Ok("foo"));
/// ```
#[stable(feature = "cstr_to_str", since = "1.4.0")]
pub fn to_str(&self) -> Result<&str, str::Utf8Error> {
#[rustc_const_stable(feature = "const_cstr_methods", since = "CURRENT_RUSTC_VERSION")]
pub const fn to_str(&self) -> Result<&str, str::Utf8Error> {
// N.B., when `CStr` is changed to perform the length check in `.to_bytes()`
// instead of in `from_ptr()`, it may be worth considering if this should
// be rewritten to do the UTF-8 check inline with the length calculation
Expand Down
1 change: 0 additions & 1 deletion library/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,6 @@
#![feature(const_caller_location)]
#![feature(const_cell_into_inner)]
#![feature(const_char_from_u32_unchecked)]
#![feature(const_cstr_methods)]
#![feature(const_discriminant)]
#![feature(const_eval_select)]
#![feature(const_exact_div)]
Expand Down

0 comments on commit 016c306

Please sign in to comment.