Change:
Feature gate: This is a tracking issue for ASCII trim functions on byte slices. The feature adds three new methods to byte slices (): : Remove leading ASCII whitespace : Remove trailing ASCII whitespace : Remove leading and trailing ASCII whitespace For deciding what bytes to treat as whitespace,  is used. See the linked docs for more details. Examples: [x] Implementation: [ ] Final comment period (FCP) [ ] Stabilization PR <!-- Once the feature has gone through a few release cycles and there are no unresolved questions left, the feature might be ready for stabilization. If this feature didn't go through the RFC process, a final comment period (FCP) is always needed before stabilization. This works as follows: A library API team member can kick off the stabilization process, at which point the rfcbot will ask all the team members to verify they agree with stabilization. Once enough members agree and there are no concerns, the final comment period begins: this issue will be marked as such and will be listed in the next This Week in Rust newsletter. If no blocking concerns are raised in that period of 10 days, a stabilzation PR can be opened by anyone. --<!-- Include any open questions that need to be answered before the feature can be stabilised. If multiple (unrelated) big questions come up, it can be a good idea to open a separate issue for each, to make it easier to keep track of the discussions. It's useful to link any relevant discussions and conclusions (whether on GitHub, Zulip, or the internals forum) here. --- [ ] Should corresponding methods be to as well?
If we're adding variants to , we should also add equivalent operations to at the same time. I feel this is pretty important because otherwise the APIs made available by libstd can come across like a grab bag of random stuff. It seems reasonable enough to want to trim ASCII whitespace from a too (there's already ), and so today one would resort to or with this implementation they could convert between and . The need to do latter can come across as a particularly frustrating API surface omission. naming bikeshed: might and be more consistent with other functions already in libstd? There's some prior art in naming of vs , vs etc.
Since is validated unicode, and could be used, which uses the unicode definition of whitespace. On the other hand, we have , so it should be fairly easy to add these functions to string slices as well. However, I don't really see a use case for this. Why would someone use over ? Yes, you have a point there! There's also and . I will update the proposal and implementation PR.
Naively (without knowing the internals at all), I could think of "performance" as a reason. That being said, does this unresolved question really need to block this from being stabilised? After all, it's specifically about , not slices in general. If we can agree to that, then what's stopping this from proceeding to FCP? (I'm having trouble finding documentation for the process for tracking issue FCPs, could only find something for RFCs...)
Both byte slices and string slices can have both Unicode-aware trimming and ASCII-aware trimming. Unicode support needn't be limited to and ASCII support needn't be limited to . With that said, I think adding these methods starts us down the path of treating byte slices as byte strings. I think I am in general in favor of that, but I do think we want to proceed deliberately here. It might make sense to block this on a higher level policy question of 1) do we want byte slices to be byte strings? and 2) if so, what do we want to add? (2) is particularly relevant to make sure we end up with good names I think.
I have a use case where I have to trim a bunch of s from the end of a byte slice. To me, -like methods for slices in all generality would be warranted. I wonder whether people agree with that?
I think these would be useful (including also on ). I often find myself writing mini-parsers that deal with identifiers and syntaxes that are definitely ASCII-only. I don't want to pay code size increase for proper Unicode processing. I also don't want to create APIs or data formats that unnecessarily allow complexity of all of Unicode whitespace.
What is the status of this feature?
I don't think that this a policy decision like this is needed. Byte slices may contain byte strings, or something else. Of course trimming ASCII whitespace results in nonsensical results, if the slices aren't actually ASCII strings, but that's all. Note that there are already other slice methods that treat bytes as potential strings, for example:      What would be needed to move this proposal into FCP?
I filed a libs ACP, which was accepted, for extending these functions to Implementation PR I was instructed to use this tracking issue.
Original PR is implemented Libs ACP is proposed and accepted for adding the trimming functions to PR that extends the trimming functions to is merged The following six functions would be stabilized: three functions on and three functions on .  is used to determine whitespace. Remove leading and trailing ASCII whitespace Remove trailing ASCII whitespace Remove leading ASCII whitespace Examples: Question: Does a policy decision need to be made about treating byte slices as byte strings? What methods should be ? Response: For more context, was stabilized in and the other four methods were stabilized in . If this is adequate to start FCP, I can make the stabilization PR.
Seems like this is more than ready for stabilization.
Nominating for T-libs-api, as there seem to be no blockers to stabilization, with a report already being written.
This overall seems reasonable to me. We've clearly already started down the path of " is sometimes a byte string." I also think we should continue to that path. fcp merge
Team member has proposed to merge this. The next step is review by the rest of the tagged team members: [ ] [x] [x] [x] [x] No concerns currently listed. Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! See for info about what commands tagged team members can give me.
:bell: This is now entering its final comment period, as per the . :bell:
The final comment period, with a disposition to merge, as per the , is now complete. As the automated representative of the governance process, I would like to thank the author for their work and everyone else who contributed. This will be merged soon.
:tada: Is this worth an explicit relnotes tag?
Definitely; I've it.

Discussion:
/// Returns a byte slice with leading ASCII whitespace bytes removed. /// /// 'Whitespace' refers to the definition used by <del> /// `u8::is_ascii_whitespace`. </del> <ins> /// [`u8::is_ascii_whitespace`]. </ins> /// /// # Examples /// /// ``` <del> /// #![feature(byte_slice_trim_ascii)] /// </del> /// assert_eq!(b" t hello worldn".trim_ascii_start(), b"hello worldn"); /// assert_eq!(b"  ".trim_ascii_start(), b""); /// assert_eq!(b"".trim_ascii_start(), b""); /// ``` <del> #[unstable(feature = "byte_slice_trim_ascii", issue = "94035")] </del> <ins> #[stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")] </ins> #[inline] pub const fn trim_ascii_start(&self) -> &[u8] { let mut bytes = self;

/// Returns a byte slice with trailing ASCII whitespace bytes removed. /// /// 'Whitespace' refers to the definition used by <del> /// `u8::is_ascii_whitespace`. </del> <ins> /// [`u8::is_ascii_whitespace`]. </ins> /// /// # Examples /// /// ``` <del> /// #![feature(byte_slice_trim_ascii)] /// </del> /// assert_eq!(b"r hello worldn ".trim_ascii_end(), b"r hello world"); /// assert_eq!(b"  ".trim_ascii_end(), b""); /// assert_eq!(b"".trim_ascii_end(), b""); /// ``` <del> #[unstable(feature = "byte_slice_trim_ascii", issue = "94035")] </del> <ins> #[stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")] </ins> #[inline] pub const fn trim_ascii_end(&self) -> &[u8] { let mut bytes = self;

/// removed. /// /// 'Whitespace' refers to the definition used by <del> /// `u8::is_ascii_whitespace`. </del> <ins> /// [`u8::is_ascii_whitespace`]. </ins> /// /// # Examples /// /// ``` <del> /// #![feature(byte_slice_trim_ascii)] /// </del> /// assert_eq!(b"r hello worldn ".trim_ascii(), b"hello world"); /// assert_eq!(b"  ".trim_ascii(), b""); /// assert_eq!(b"".trim_ascii(), b""); /// ``` <del> #[unstable(feature = "byte_slice_trim_ascii", issue = "94035")] </del> <ins> #[stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")] </ins> #[inline] pub const fn trim_ascii(&self) -> &[u8] { self.trim_ascii_start().trim_ascii_end()

/// # Examples /// /// ``` <del> /// #![feature(byte_slice_trim_ascii)] /// </del> /// assert_eq!(" t u{3000}hello worldn".trim_ascii_start(), "u{3000}hello worldn"); /// assert_eq!("  ".trim_ascii_start(), ""); /// assert_eq!("".trim_ascii_start(), ""); /// ``` <del> #[unstable(feature = "byte_slice_trim_ascii", issue = "94035")] </del> #[must_use = "this returns the trimmed string as a new slice,  without modifying the original"] <ins> #[stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")] </ins> #[inline] pub const fn trim_ascii_start(&self) -> &str { // SAFETY: Removing ASCII characters from a `&str` does not invalidate

/// # Examples /// /// ``` <del> /// #![feature(byte_slice_trim_ascii)] /// </del> /// assert_eq!("r hello worldu{3000}n ".trim_ascii_end(), "r hello worldu{3000}"); /// assert_eq!("  ".trim_ascii_end(), ""); /// assert_eq!("".trim_ascii_end(), ""); /// ``` <del> #[unstable(feature = "byte_slice_trim_ascii", issue = "94035")] </del> #[must_use = "this returns the trimmed string as a new slice,  without modifying the original"] <ins> #[stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")] </ins> #[inline] pub const fn trim_ascii_end(&self) -> &str { // SAFETY: Removing ASCII characters from a `&str` does not invalidate

/// # Examples /// /// ``` <del> /// #![feature(byte_slice_trim_ascii)] /// </del> /// assert_eq!("r hello worldn ".trim_ascii(), "hello world"); /// assert_eq!("  ".trim_ascii(), ""); /// assert_eq!("".trim_ascii(), ""); /// ``` <del> #[unstable(feature = "byte_slice_trim_ascii", issue = "94035")] </del> #[must_use = "this returns the trimmed string as a new slice,  without modifying the original"] <ins> #[stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")] </ins> #[inline] pub const fn trim_ascii(&self) -> &str { // SAFETY: Removing ASCII characters from a `&str` does not invalidate