diff --git a/core/src/internal_macros.rs b/core/src/internal_macros.rs index d3a4d6aff2d8b..bf53b2245ac59 100644 --- a/core/src/internal_macros.rs +++ b/core/src/internal_macros.rs @@ -80,6 +80,47 @@ macro_rules! forward_ref_op_assign { } } +/// Create a zero-size type similar to a closure type, but named. +macro_rules! impl_fn_for_zst { + ($( + $( #[$attr: meta] )* + struct $Name: ident impl$( <$( $lifetime : lifetime ),+> )? Fn = + |$( $arg: ident: $ArgTy: ty ),*| -> $ReturnTy: ty + $body: block; + )+) => { + $( + $( #[$attr] )* + struct $Name; + + impl $( <$( $lifetime ),+> )? Fn<($( $ArgTy, )*)> for $Name { + #[inline] + extern "rust-call" fn call(&self, ($( $arg, )*): ($( $ArgTy, )*)) -> $ReturnTy { + $body + } + } + + impl $( <$( $lifetime ),+> )? FnMut<($( $ArgTy, )*)> for $Name { + #[inline] + extern "rust-call" fn call_mut( + &mut self, + ($( $arg, )*): ($( $ArgTy, )*) + ) -> $ReturnTy { + Fn::call(&*self, ($( $arg, )*)) + } + } + + impl $( <$( $lifetime ),+> )? FnOnce<($( $ArgTy, )*)> for $Name { + type Output = $ReturnTy; + + #[inline] + extern "rust-call" fn call_once(self, ($( $arg, )*): ($( $ArgTy, )*)) -> $ReturnTy { + Fn::call(&self, ($( $arg, )*)) + } + } + )+ + } +} + /// A macro for defining `#[cfg]` if-else statements. /// /// `cfg_if` is similar to the `if/elif` C preprocessor macro by allowing definition of a cascade diff --git a/core/src/lib.rs b/core/src/lib.rs index 89642d6226a24..ef28bc99c4fc8 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -255,7 +255,6 @@ #![feature(trait_alias)] #![feature(transparent_unions)] #![feature(try_blocks)] -#![feature(type_alias_impl_trait)] #![feature(unboxed_closures)] #![feature(unsized_fn_params)] #![feature(with_negative_coherence)] diff --git a/core/src/slice/ascii.rs b/core/src/slice/ascii.rs index 19c91ba2eb988..8ad045275adec 100644 --- a/core/src/slice/ascii.rs +++ b/core/src/slice/ascii.rs @@ -108,7 +108,7 @@ impl [u8] { without modifying the original"] #[stable(feature = "inherent_ascii_escape", since = "1.60.0")] pub fn escape_ascii(&self) -> EscapeAscii<'_> { - EscapeAscii { inner: self.iter().flat_map(|byte| byte.escape_ascii()) } + EscapeAscii { inner: self.iter().flat_map(EscapeByte) } } /// Returns a byte slice with leading ASCII whitespace bytes removed. @@ -190,7 +190,12 @@ impl [u8] { } } -type EscapeByte = impl (Fn(&u8) -> ascii::EscapeDefault) + Copy; +impl_fn_for_zst! { + #[derive(Clone)] + struct EscapeByte impl Fn = |byte: &u8| -> ascii::EscapeDefault { + ascii::escape_default(*byte) + }; +} /// An iterator over the escaped version of a byte slice. /// diff --git a/core/src/str/iter.rs b/core/src/str/iter.rs index d61f04102e5e5..19627f28e64f8 100644 --- a/core/src/str/iter.rs +++ b/core/src/str/iter.rs @@ -1274,10 +1274,8 @@ pub struct SplitWhitespace<'a> { #[stable(feature = "split_ascii_whitespace", since = "1.34.0")] #[derive(Clone, Debug)] pub struct SplitAsciiWhitespace<'a> { - pub(super) inner: Map< - Filter, BytesIsNotEmpty<'a>>, - UnsafeBytesToStr<'a>, - >, + pub(super) inner: + Map, BytesIsNotEmpty>, UnsafeBytesToStr>, } /// An iterator over the substrings of a string, diff --git a/core/src/str/mod.rs b/core/src/str/mod.rs index edda4d1b68703..669cdc92e3586 100644 --- a/core/src/str/mod.rs +++ b/core/src/str/mod.rs @@ -983,7 +983,7 @@ impl str { #[cfg_attr(not(test), rustc_diagnostic_item = "str_split_whitespace")] #[inline] pub fn split_whitespace(&self) -> SplitWhitespace<'_> { - SplitWhitespace { inner: self.split(char::is_whitespace).filter(|s| !s.is_empty()) } + SplitWhitespace { inner: self.split(IsWhitespace).filter(IsNotEmpty) } } /// Splits a string slice by ASCII whitespace. @@ -1032,13 +1032,8 @@ impl str { #[stable(feature = "split_ascii_whitespace", since = "1.34.0")] #[inline] pub fn split_ascii_whitespace(&self) -> SplitAsciiWhitespace<'_> { - let inner = self - .as_bytes() - .split(u8::is_ascii_whitespace) - .filter(|s| !s.is_empty()) - // SAFETY: the byte slice came from a string and was only split - // along character boundaries, so the resulting slices are strings. - .map(|bytes| unsafe { from_utf8_unchecked(bytes) }); + let inner = + self.as_bytes().split(IsAsciiWhitespace).filter(BytesIsNotEmpty).map(UnsafeBytesToStr); SplitAsciiWhitespace { inner } } @@ -1090,11 +1085,7 @@ impl str { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn lines(&self) -> Lines<'_> { - Lines(self.split_inclusive('\n').map(|line| { - let Some(line) = line.strip_suffix('\n') else { return line }; - let Some(line) = line.strip_suffix('\r') else { return line }; - line - })) + Lines(self.split_inclusive('\n').map(LinesMap)) } /// An iterator over the lines of a string. @@ -2645,19 +2636,14 @@ impl str { #[stable(feature = "str_escape", since = "1.34.0")] pub fn escape_debug(&self) -> EscapeDebug<'_> { let mut chars = self.chars(); - let first = chars - .next() - .map(|first| first.escape_debug_ext(EscapeDebugExtArgs::ESCAPE_ALL)) - .into_iter() - .flatten(); - let inner = first.chain(chars.flat_map(|c| { - c.escape_debug_ext(EscapeDebugExtArgs { - escape_grapheme_extended: false, - escape_single_quote: true, - escape_double_quote: true, - }) - })); - EscapeDebug { inner } + EscapeDebug { + inner: chars + .next() + .map(|first| first.escape_debug_ext(EscapeDebugExtArgs::ESCAPE_ALL)) + .into_iter() + .flatten() + .chain(chars.flat_map(CharEscapeDebugContinue)), + } } /// Return an iterator that escapes each char in `self` with [`char::escape_default`]. @@ -2695,7 +2681,7 @@ impl str { without modifying the original"] #[stable(feature = "str_escape", since = "1.34.0")] pub fn escape_default(&self) -> EscapeDefault<'_> { - EscapeDefault { inner: self.chars().flat_map(char::escape_default) } + EscapeDefault { inner: self.chars().flat_map(CharEscapeDefault) } } /// Return an iterator that escapes each char in `self` with [`char::escape_unicode`]. @@ -2733,7 +2719,7 @@ impl str { without modifying the original"] #[stable(feature = "str_escape", since = "1.34.0")] pub fn escape_unicode(&self) -> EscapeUnicode<'_> { - EscapeUnicode { inner: self.chars().flat_map(char::escape_unicode) } + EscapeUnicode { inner: self.chars().flat_map(CharEscapeUnicode) } } } @@ -2764,15 +2750,59 @@ impl Default for &mut str { } } -type LinesMap = impl (Fn(&str) -> &str) + Copy; -type CharEscapeDebugContinue = impl (FnMut(char) -> char::EscapeDebug) + Copy; -type CharEscapeUnicode = impl (Fn(char) -> char::EscapeUnicode) + Copy; -type CharEscapeDefault = impl (Fn(char) -> char::EscapeDefault) + Copy; -type IsWhitespace = impl (Fn(char) -> bool) + Copy; -type IsAsciiWhitespace = impl (Fn(&u8) -> bool) + Copy; -type IsNotEmpty = impl (Fn(&&str) -> bool) + Copy; -type BytesIsNotEmpty<'a> = impl (FnMut(&&'a [u8]) -> bool) + Copy; -type UnsafeBytesToStr<'a> = impl (FnMut(&'a [u8]) -> &'a str) + Copy; +impl_fn_for_zst! { + /// A nameable, cloneable fn type + #[derive(Clone)] + struct LinesMap impl<'a> Fn = |line: &'a str| -> &'a str { + let Some(line) = line.strip_suffix('\n') else { return line }; + let Some(line) = line.strip_suffix('\r') else { return line }; + line + }; + + #[derive(Clone)] + struct CharEscapeDebugContinue impl Fn = |c: char| -> char::EscapeDebug { + c.escape_debug_ext(EscapeDebugExtArgs { + escape_grapheme_extended: false, + escape_single_quote: true, + escape_double_quote: true + }) + }; + + #[derive(Clone)] + struct CharEscapeUnicode impl Fn = |c: char| -> char::EscapeUnicode { + c.escape_unicode() + }; + #[derive(Clone)] + struct CharEscapeDefault impl Fn = |c: char| -> char::EscapeDefault { + c.escape_default() + }; + + #[derive(Clone)] + struct IsWhitespace impl Fn = |c: char| -> bool { + c.is_whitespace() + }; + + #[derive(Clone)] + struct IsAsciiWhitespace impl Fn = |byte: &u8| -> bool { + byte.is_ascii_whitespace() + }; + + #[derive(Clone)] + struct IsNotEmpty impl<'a, 'b> Fn = |s: &'a &'b str| -> bool { + !s.is_empty() + }; + + #[derive(Clone)] + struct BytesIsNotEmpty impl<'a, 'b> Fn = |s: &'a &'b [u8]| -> bool { + !s.is_empty() + }; + + #[derive(Clone)] + struct UnsafeBytesToStr impl<'a> Fn = |bytes: &'a [u8]| -> &'a str { + // SAFETY: not safe + unsafe { from_utf8_unchecked(bytes) } + }; +} // This is required to make `impl From<&str> for Box` and `impl From for Box` not overlap. #[stable(feature = "rust1", since = "1.0.0")]