Skip to content

Commit 7a07780

Browse files
committed
New return types for str::escape_* that impl Display and Iterator<char>
As FCP’ed in the tracking issue: rust-lang#27791 (comment)
1 parent 92dcae4 commit 7a07780

File tree

6 files changed

+132
-46
lines changed

6 files changed

+132
-46
lines changed

src/liballoc/lib.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@
6868
#![warn(intra_doc_link_resolution_failure)]
6969
#![warn(missing_debug_implementations)]
7070

71-
#![cfg_attr(not(test), feature(fn_traits))]
7271
#![cfg_attr(not(test), feature(generator_trait))]
7372
#![cfg_attr(test, feature(test))]
7473

@@ -86,6 +85,7 @@
8685
#![feature(dropck_eyepatch)]
8786
#![feature(exact_size_is_empty)]
8887
#![feature(fmt_internals)]
88+
#![feature(fn_traits)]
8989
#![feature(fundamental)]
9090
#![feature(futures_api)]
9191
#![feature(lang_items)]
@@ -100,6 +100,7 @@
100100
#![feature(receiver_trait)]
101101
#![feature(specialization)]
102102
#![feature(staged_api)]
103+
#![feature(std_internals)]
103104
#![feature(str_internals)]
104105
#![feature(trusted_len)]
105106
#![feature(try_reserve)]

src/liballoc/str.rs

+96-12
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,13 @@
2929
#![allow(unused_imports)]
3030

3131
use core::borrow::Borrow;
32-
use core::fmt;
33-
use core::str as core_str;
32+
use core::fmt::{self, Write};
33+
use core::char;
34+
use core::iter::{Chain, Flatten, FlatMap};
3435
use core::str::pattern::{Pattern, Searcher, ReverseSearcher, DoubleEndedSearcher};
3536
use core::mem;
37+
use core::ops::Try;
38+
use core::option;
3639
use core::ptr;
3740
use core::iter::FusedIterator;
3841
use core::unicode::conversions;
@@ -452,14 +455,15 @@ impl str {
452455
#[unstable(feature = "str_escape",
453456
reason = "return type may change to be an iterator",
454457
issue = "27791")]
455-
pub fn escape_debug(&self) -> String {
456-
let mut string = String::with_capacity(self.len());
458+
pub fn escape_debug(&self) -> EscapeDebug {
457459
let mut chars = self.chars();
458-
if let Some(first) = chars.next() {
459-
string.extend(first.escape_debug_ext(true))
460+
EscapeDebug {
461+
inner: chars.next()
462+
.map(|first| first.escape_debug_ext(true))
463+
.into_iter()
464+
.flatten()
465+
.chain(chars.flat_map(CharEscapeDebugContinue))
460466
}
461-
string.extend(chars.flat_map(|c| c.escape_debug_ext(false)));
462-
string
463467
}
464468

465469
/// Escapes each char in `s` with [`char::escape_default`].
@@ -468,8 +472,8 @@ impl str {
468472
#[unstable(feature = "str_escape",
469473
reason = "return type may change to be an iterator",
470474
issue = "27791")]
471-
pub fn escape_default(&self) -> String {
472-
self.chars().flat_map(|c| c.escape_default()).collect()
475+
pub fn escape_default(&self) -> EscapeDefault {
476+
EscapeDefault { inner: self.chars().flat_map(CharEscapeDefault) }
473477
}
474478

475479
/// Escapes each char in `s` with [`char::escape_unicode`].
@@ -478,8 +482,8 @@ impl str {
478482
#[unstable(feature = "str_escape",
479483
reason = "return type may change to be an iterator",
480484
issue = "27791")]
481-
pub fn escape_unicode(&self) -> String {
482-
self.chars().flat_map(|c| c.escape_unicode()).collect()
485+
pub fn escape_unicode(&self) -> EscapeUnicode {
486+
EscapeUnicode { inner: self.chars().flat_map(CharEscapeUnicode) }
483487
}
484488

485489
/// Converts a [`Box<str>`] into a [`String`] without copying or allocating.
@@ -612,3 +616,83 @@ impl str {
612616
pub unsafe fn from_boxed_utf8_unchecked(v: Box<[u8]>) -> Box<str> {
613617
Box::from_raw(Box::into_raw(v) as *mut str)
614618
}
619+
620+
impl_fn_for_zst! {
621+
#[derive(Clone)]
622+
struct CharEscapeDebugContinue impl Fn = |c: char| -> char::EscapeDebug {
623+
c.escape_debug_ext(false)
624+
};
625+
626+
#[derive(Clone)]
627+
struct CharEscapeUnicode impl Fn = |c: char| -> char::EscapeUnicode {
628+
c.escape_unicode()
629+
};
630+
#[derive(Clone)]
631+
struct CharEscapeDefault impl Fn = |c: char| -> char::EscapeDefault {
632+
c.escape_default()
633+
};
634+
}
635+
636+
macro_rules! escape_types {
637+
($(
638+
struct $Name: ident<'a> {
639+
inner: $Inner: ty,
640+
}
641+
)+) => {$(
642+
#[unstable(feature = "str_escape", issue = "27791")]
643+
#[derive(Clone, Debug)]
644+
pub struct $Name<'a> {
645+
inner: $Inner,
646+
}
647+
648+
#[unstable(feature = "str_escape", issue = "27791")]
649+
impl<'a> fmt::Display for $Name<'a> {
650+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
651+
self.clone().try_for_each(|c| f.write_char(c))
652+
}
653+
}
654+
655+
#[unstable(feature = "str_escape", issue = "27791")]
656+
impl<'a> Iterator for $Name<'a> {
657+
type Item = char;
658+
659+
#[inline]
660+
fn next(&mut self) -> Option<char> { self.inner.next() }
661+
662+
#[inline]
663+
fn size_hint(&self) -> (usize, Option<usize>) { self.inner.size_hint() }
664+
665+
#[inline]
666+
fn try_fold<Acc, Fold, R>(&mut self, init: Acc, fold: Fold) -> R where
667+
Self: Sized, Fold: FnMut(Acc, Self::Item) -> R, R: Try<Ok=Acc>
668+
{
669+
self.inner.try_fold(init, fold)
670+
}
671+
672+
#[inline]
673+
fn fold<Acc, Fold>(self, init: Acc, fold: Fold) -> Acc
674+
where Fold: FnMut(Acc, Self::Item) -> Acc,
675+
{
676+
self.inner.fold(init, fold)
677+
}
678+
}
679+
680+
#[unstable(feature = "str_escape", issue = "27791")]
681+
impl<'a> FusedIterator for $Name<'a> {}
682+
)+}
683+
}
684+
685+
escape_types! {
686+
struct EscapeDebug<'a> {
687+
inner: Chain<
688+
Flatten<option::IntoIter<char::EscapeDebug>>,
689+
FlatMap<Chars<'a>, char::EscapeDebug, CharEscapeDebugContinue>
690+
>,
691+
}
692+
struct EscapeUnicode<'a> {
693+
inner: FlatMap<Chars<'a>, char::EscapeUnicode, CharEscapeUnicode>,
694+
}
695+
struct EscapeDefault<'a> {
696+
inner: FlatMap<Chars<'a>, char::EscapeDefault, CharEscapeDefault>,
697+
}
698+
}

src/liballoc/tests/str.rs

+31-30
Original file line numberDiff line numberDiff line change
@@ -990,15 +990,15 @@ fn test_split_at_boundscheck() {
990990

991991
#[test]
992992
fn test_escape_unicode() {
993-
assert_eq!("abc".escape_unicode(), "\\u{61}\\u{62}\\u{63}");
994-
assert_eq!("a c".escape_unicode(), "\\u{61}\\u{20}\\u{63}");
995-
assert_eq!("\r\n\t".escape_unicode(), "\\u{d}\\u{a}\\u{9}");
996-
assert_eq!("'\"\\".escape_unicode(), "\\u{27}\\u{22}\\u{5c}");
997-
assert_eq!("\x00\x01\u{fe}\u{ff}".escape_unicode(), "\\u{0}\\u{1}\\u{fe}\\u{ff}");
998-
assert_eq!("\u{100}\u{ffff}".escape_unicode(), "\\u{100}\\u{ffff}");
999-
assert_eq!("\u{10000}\u{10ffff}".escape_unicode(), "\\u{10000}\\u{10ffff}");
1000-
assert_eq!("ab\u{fb00}".escape_unicode(), "\\u{61}\\u{62}\\u{fb00}");
1001-
assert_eq!("\u{1d4ea}\r".escape_unicode(), "\\u{1d4ea}\\u{d}");
993+
assert_eq!("abc".escape_unicode().to_string(), "\\u{61}\\u{62}\\u{63}");
994+
assert_eq!("a c".escape_unicode().to_string(), "\\u{61}\\u{20}\\u{63}");
995+
assert_eq!("\r\n\t".escape_unicode().to_string(), "\\u{d}\\u{a}\\u{9}");
996+
assert_eq!("'\"\\".escape_unicode().to_string(), "\\u{27}\\u{22}\\u{5c}");
997+
assert_eq!("\x00\x01\u{fe}\u{ff}".escape_unicode().to_string(), "\\u{0}\\u{1}\\u{fe}\\u{ff}");
998+
assert_eq!("\u{100}\u{ffff}".escape_unicode().to_string(), "\\u{100}\\u{ffff}");
999+
assert_eq!("\u{10000}\u{10ffff}".escape_unicode().to_string(), "\\u{10000}\\u{10ffff}");
1000+
assert_eq!("ab\u{fb00}".escape_unicode().to_string(), "\\u{61}\\u{62}\\u{fb00}");
1001+
assert_eq!("\u{1d4ea}\r".escape_unicode().to_string(), "\\u{1d4ea}\\u{d}");
10021002
}
10031003

10041004
#[test]
@@ -1009,31 +1009,32 @@ fn test_escape_debug() {
10091009
// they are escaped. However, when the character is unescaped (e.g., for
10101010
// printable characters), only a single backslash appears (as the character
10111011
// itself appears in the debug string).
1012-
assert_eq!("abc".escape_debug(), "abc");
1013-
assert_eq!("a c".escape_debug(), "a c");
1014-
assert_eq!("éèê".escape_debug(), "éèê");
1015-
assert_eq!("\r\n\t".escape_debug(), "\\r\\n\\t");
1016-
assert_eq!("'\"\\".escape_debug(), "\\'\\\"\\\\");
1017-
assert_eq!("\u{7f}\u{ff}".escape_debug(), "\\u{7f}\u{ff}");
1018-
assert_eq!("\u{100}\u{ffff}".escape_debug(), "\u{100}\\u{ffff}");
1019-
assert_eq!("\u{10000}\u{10ffff}".escape_debug(), "\u{10000}\\u{10ffff}");
1020-
assert_eq!("ab\u{200b}".escape_debug(), "ab\\u{200b}");
1021-
assert_eq!("\u{10d4ea}\r".escape_debug(), "\\u{10d4ea}\\r");
1022-
assert_eq!("\u{301}a\u{301}\u{e000}".escape_debug(), "\\u{301}a\u{301}\\u{e000}");
1012+
assert_eq!("abc".escape_debug().to_string(), "abc");
1013+
assert_eq!("a c".escape_debug().to_string(), "a c");
1014+
assert_eq!("éèê".escape_debug().to_string(), "éèê");
1015+
assert_eq!("\r\n\t".escape_debug().to_string(), "\\r\\n\\t");
1016+
assert_eq!("'\"\\".escape_debug().to_string(), "\\'\\\"\\\\");
1017+
assert_eq!("\u{7f}\u{ff}".escape_debug().to_string(), "\\u{7f}\u{ff}");
1018+
assert_eq!("\u{100}\u{ffff}".escape_debug().to_string(), "\u{100}\\u{ffff}");
1019+
assert_eq!("\u{10000}\u{10ffff}".escape_debug().to_string(), "\u{10000}\\u{10ffff}");
1020+
assert_eq!("ab\u{200b}".escape_debug().to_string(), "ab\\u{200b}");
1021+
assert_eq!("\u{10d4ea}\r".escape_debug().to_string(), "\\u{10d4ea}\\r");
1022+
assert_eq!("\u{301}a\u{301}\u{e000}".escape_debug().to_string(),
1023+
"\\u{301}a\u{301}\\u{e000}");
10231024
}
10241025

10251026
#[test]
10261027
fn test_escape_default() {
1027-
assert_eq!("abc".escape_default(), "abc");
1028-
assert_eq!("a c".escape_default(), "a c");
1029-
assert_eq!("éèê".escape_default(), "\\u{e9}\\u{e8}\\u{ea}");
1030-
assert_eq!("\r\n\t".escape_default(), "\\r\\n\\t");
1031-
assert_eq!("'\"\\".escape_default(), "\\'\\\"\\\\");
1032-
assert_eq!("\u{7f}\u{ff}".escape_default(), "\\u{7f}\\u{ff}");
1033-
assert_eq!("\u{100}\u{ffff}".escape_default(), "\\u{100}\\u{ffff}");
1034-
assert_eq!("\u{10000}\u{10ffff}".escape_default(), "\\u{10000}\\u{10ffff}");
1035-
assert_eq!("ab\u{200b}".escape_default(), "ab\\u{200b}");
1036-
assert_eq!("\u{10d4ea}\r".escape_default(), "\\u{10d4ea}\\r");
1028+
assert_eq!("abc".escape_default().to_string(), "abc");
1029+
assert_eq!("a c".escape_default().to_string(), "a c");
1030+
assert_eq!("éèê".escape_default().to_string(), "\\u{e9}\\u{e8}\\u{ea}");
1031+
assert_eq!("\r\n\t".escape_default().to_string(), "\\r\\n\\t");
1032+
assert_eq!("'\"\\".escape_default().to_string(), "\\'\\\"\\\\");
1033+
assert_eq!("\u{7f}\u{ff}".escape_default().to_string(), "\\u{7f}\\u{ff}");
1034+
assert_eq!("\u{100}\u{ffff}".escape_default().to_string(), "\\u{100}\\u{ffff}");
1035+
assert_eq!("\u{10000}\u{10ffff}".escape_default().to_string(), "\\u{10000}\\u{10ffff}");
1036+
assert_eq!("ab\u{200b}".escape_default().to_string(), "ab\\u{200b}");
1037+
assert_eq!("\u{10d4ea}\r".escape_default().to_string(), "\\u{10d4ea}\\r");
10371038
}
10381039

10391040
#[test]

src/libgraphviz/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -538,7 +538,7 @@ impl<'a> LabelText<'a> {
538538
EscStr(s) => s,
539539
LabelStr(s) => {
540540
if s.contains('\\') {
541-
(&*s).escape_default().into()
541+
(&*s).escape_default().to_string().into()
542542
} else {
543543
s
544544
}

src/libsyntax/attr/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -634,7 +634,7 @@ impl LitKind {
634634

635635
match *self {
636636
LitKind::Str(string, ast::StrStyle::Cooked) => {
637-
let escaped = string.as_str().escape_default();
637+
let escaped = string.as_str().escape_default().to_string();
638638
Token::Literal(token::Lit::Str_(Symbol::intern(&escaped)), None)
639639
}
640640
LitKind::Str(string, ast::StrStyle::Raw(n)) => {

src/libsyntax/print/pprust.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -606,7 +606,7 @@ pub trait PrintState<'a> {
606606
match lit.node {
607607
ast::LitKind::Str(st, style) => self.print_string(&st.as_str(), style),
608608
ast::LitKind::Err(st) => {
609-
let st = st.as_str().escape_debug();
609+
let st = st.as_str().escape_debug().to_string();
610610
let mut res = String::with_capacity(st.len() + 2);
611611
res.push('\'');
612612
res.push_str(&st);

0 commit comments

Comments
 (0)