Skip to content

Commit dd46cf8

Browse files
committed
Auto merge of #26241 - SimonSapin:derefmut-for-string, r=alexcrichton
See rust-lang/rfcs#1157
2 parents 72483f5 + 3226858 commit dd46cf8

File tree

8 files changed

+213
-27
lines changed

8 files changed

+213
-27
lines changed

src/libcollections/str.rs

+17-1
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,14 @@ impl str {
550550
core_str::StrExt::slice_unchecked(self, begin, end)
551551
}
552552

553+
/// Takes a bytewise mutable slice from a string.
554+
///
555+
/// Same as `slice_unchecked`, but works with `&mut str` instead of `&str`.
556+
#[unstable(feature = "str_slice_mut", reason = "recently added")]
557+
pub unsafe fn slice_mut_unchecked(&mut self, begin: usize, end: usize) -> &mut str {
558+
core_str::StrExt::slice_mut_unchecked(self, begin, end)
559+
}
560+
553561
/// Returns a slice of the string from the character range [`begin`..`end`).
554562
///
555563
/// That is, start at the `begin`-th code point of the string and continue
@@ -776,7 +784,7 @@ impl str {
776784
///
777785
/// # Examples
778786
/// ```
779-
/// # #![feature(collections)]
787+
/// # #![feature(str_split_at)]
780788
/// let s = "Löwe 老虎 Léopard";
781789
/// let first_space = s.find(' ').unwrap_or(s.len());
782790
/// let (a, b) = s.split_at(first_space);
@@ -785,10 +793,18 @@ impl str {
785793
/// assert_eq!(b, " 老虎 Léopard");
786794
/// ```
787795
#[inline]
796+
#[unstable(feature = "str_split_at", reason = "recently added")]
788797
pub fn split_at(&self, mid: usize) -> (&str, &str) {
789798
core_str::StrExt::split_at(self, mid)
790799
}
791800

801+
/// Divide one mutable string slice into two at an index.
802+
#[inline]
803+
#[unstable(feature = "str_split_at", reason = "recently added")]
804+
pub fn split_at_mut(&mut self, mid: usize) -> (&mut str, &mut str) {
805+
core_str::StrExt::split_at_mut(self, mid)
806+
}
807+
792808
/// An iterator over the codepoints of `self`.
793809
///
794810
/// # Examples

src/libcollections/string.rs

+40
Original file line numberDiff line numberDiff line change
@@ -979,6 +979,38 @@ impl ops::Index<ops::RangeFull> for String {
979979
}
980980
}
981981

982+
#[cfg(not(stage0))]
983+
#[stable(feature = "derefmut_for_string", since = "1.2.0")]
984+
impl ops::IndexMut<ops::Range<usize>> for String {
985+
#[inline]
986+
fn index_mut(&mut self, index: ops::Range<usize>) -> &mut str {
987+
&mut self[..][index]
988+
}
989+
}
990+
#[cfg(not(stage0))]
991+
#[stable(feature = "derefmut_for_string", since = "1.2.0")]
992+
impl ops::IndexMut<ops::RangeTo<usize>> for String {
993+
#[inline]
994+
fn index_mut(&mut self, index: ops::RangeTo<usize>) -> &mut str {
995+
&mut self[..][index]
996+
}
997+
}
998+
#[cfg(not(stage0))]
999+
#[stable(feature = "derefmut_for_string", since = "1.2.0")]
1000+
impl ops::IndexMut<ops::RangeFrom<usize>> for String {
1001+
#[inline]
1002+
fn index_mut(&mut self, index: ops::RangeFrom<usize>) -> &mut str {
1003+
&mut self[..][index]
1004+
}
1005+
}
1006+
#[stable(feature = "derefmut_for_string", since = "1.2.0")]
1007+
impl ops::IndexMut<ops::RangeFull> for String {
1008+
#[inline]
1009+
fn index_mut(&mut self, _index: ops::RangeFull) -> &mut str {
1010+
unsafe { mem::transmute(&mut *self.vec) }
1011+
}
1012+
}
1013+
9821014
#[stable(feature = "rust1", since = "1.0.0")]
9831015
impl ops::Deref for String {
9841016
type Target = str;
@@ -989,6 +1021,14 @@ impl ops::Deref for String {
9891021
}
9901022
}
9911023

1024+
#[stable(feature = "derefmut_for_string", since = "1.2.0")]
1025+
impl ops::DerefMut for String {
1026+
#[inline]
1027+
fn deref_mut(&mut self) -> &mut str {
1028+
unsafe { mem::transmute(&mut self.vec[..]) }
1029+
}
1030+
}
1031+
9921032
/// Wrapper type providing a `&String` reference via `Deref`.
9931033
#[unstable(feature = "collections")]
9941034
#[deprecated(since = "1.2.0",

src/libcollectionstest/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
#![feature(ascii)]
1112
#![feature(append)]
1213
#![feature(bitset)]
1314
#![feature(bitvec)]
@@ -43,6 +44,7 @@
4344
#![feature(str_char)]
4445
#![feature(str_escape)]
4546
#![feature(str_match_indices)]
47+
#![feature(str_split_at)]
4648
#![feature(str_utf16)]
4749
#![feature(box_str)]
4850
#![feature(subslice_offset)]

src/libcollectionstest/str.rs

+12
Original file line numberDiff line numberDiff line change
@@ -701,6 +701,18 @@ fn test_split_at() {
701701
assert_eq!(b, "");
702702
}
703703

704+
#[test]
705+
fn test_split_at_mut() {
706+
use std::ascii::AsciiExt;
707+
let mut s = "Hello World".to_string();
708+
{
709+
let (a, b) = s.split_at_mut(5);
710+
a.make_ascii_uppercase();
711+
b.make_ascii_lowercase();
712+
}
713+
assert_eq!(s, "HELLO world");
714+
}
715+
704716
#[test]
705717
#[should_panic]
706718
fn test_split_at_boundscheck() {

src/libcore/str/mod.rs

+79
Original file line numberDiff line numberDiff line change
@@ -1116,6 +1116,23 @@ mod traits {
11161116
}
11171117
}
11181118

1119+
/// Returns a mutable slice of the given string from the byte range
1120+
/// [`begin`..`end`).
1121+
#[stable(feature = "derefmut_for_string", since = "1.2.0")]
1122+
impl ops::IndexMut<ops::Range<usize>> for str {
1123+
#[inline]
1124+
fn index_mut(&mut self, index: ops::Range<usize>) -> &mut str {
1125+
// is_char_boundary checks that the index is in [0, .len()]
1126+
if index.start <= index.end &&
1127+
self.is_char_boundary(index.start) &&
1128+
self.is_char_boundary(index.end) {
1129+
unsafe { self.slice_mut_unchecked(index.start, index.end) }
1130+
} else {
1131+
super::slice_error_fail(self, index.start, index.end)
1132+
}
1133+
}
1134+
}
1135+
11191136
/// Returns a slice of the string from the beginning to byte
11201137
/// `end`.
11211138
///
@@ -1138,6 +1155,21 @@ mod traits {
11381155
}
11391156
}
11401157

1158+
/// Returns a mutable slice of the string from the beginning to byte
1159+
/// `end`.
1160+
#[stable(feature = "derefmut_for_string", since = "1.2.0")]
1161+
impl ops::IndexMut<ops::RangeTo<usize>> for str {
1162+
#[inline]
1163+
fn index_mut(&mut self, index: ops::RangeTo<usize>) -> &mut str {
1164+
// is_char_boundary checks that the index is in [0, .len()]
1165+
if self.is_char_boundary(index.end) {
1166+
unsafe { self.slice_mut_unchecked(0, index.end) }
1167+
} else {
1168+
super::slice_error_fail(self, 0, index.end)
1169+
}
1170+
}
1171+
}
1172+
11411173
/// Returns a slice of the string from `begin` to its end.
11421174
///
11431175
/// Equivalent to `self[begin .. self.len()]`.
@@ -1159,6 +1191,21 @@ mod traits {
11591191
}
11601192
}
11611193

1194+
/// Returns a slice of the string from `begin` to its end.
1195+
#[stable(feature = "derefmut_for_string", since = "1.2.0")]
1196+
impl ops::IndexMut<ops::RangeFrom<usize>> for str {
1197+
#[inline]
1198+
fn index_mut(&mut self, index: ops::RangeFrom<usize>) -> &mut str {
1199+
// is_char_boundary checks that the index is in [0, .len()]
1200+
if self.is_char_boundary(index.start) {
1201+
let len = self.len();
1202+
unsafe { self.slice_mut_unchecked(index.start, len) }
1203+
} else {
1204+
super::slice_error_fail(self, index.start, self.len())
1205+
}
1206+
}
1207+
}
1208+
11621209
#[stable(feature = "rust1", since = "1.0.0")]
11631210
impl ops::Index<ops::RangeFull> for str {
11641211
type Output = str;
@@ -1168,6 +1215,14 @@ mod traits {
11681215
self
11691216
}
11701217
}
1218+
1219+
#[stable(feature = "derefmut_for_string", since = "1.2.0")]
1220+
impl ops::IndexMut<ops::RangeFull> for str {
1221+
#[inline]
1222+
fn index_mut(&mut self, _index: ops::RangeFull) -> &mut str {
1223+
self
1224+
}
1225+
}
11711226
}
11721227

11731228
/// Methods for string slices
@@ -1204,6 +1259,7 @@ pub trait StrExt {
12041259
fn char_len(&self) -> usize;
12051260
fn slice_chars<'a>(&'a self, begin: usize, end: usize) -> &'a str;
12061261
unsafe fn slice_unchecked<'a>(&'a self, begin: usize, end: usize) -> &'a str;
1262+
unsafe fn slice_mut_unchecked<'a>(&'a mut self, begin: usize, end: usize) -> &'a mut str;
12071263
fn starts_with<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool;
12081264
fn ends_with<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool
12091265
where P::Searcher: ReverseSearcher<'a>;
@@ -1223,6 +1279,7 @@ pub trait StrExt {
12231279
where P::Searcher: ReverseSearcher<'a>;
12241280
fn find_str<'a, P: Pattern<'a>>(&'a self, pat: P) -> Option<usize>;
12251281
fn split_at(&self, mid: usize) -> (&str, &str);
1282+
fn split_at_mut(&mut self, mid: usize) -> (&mut str, &mut str);
12261283
fn slice_shift_char<'a>(&'a self) -> Option<(char, &'a str)>;
12271284
fn subslice_offset(&self, inner: &str) -> usize;
12281285
fn as_ptr(&self) -> *const u8;
@@ -1379,6 +1436,14 @@ impl StrExt for str {
13791436
})
13801437
}
13811438

1439+
#[inline]
1440+
unsafe fn slice_mut_unchecked(&mut self, begin: usize, end: usize) -> &mut str {
1441+
mem::transmute(Slice {
1442+
data: self.as_ptr().offset(begin as isize),
1443+
len: end - begin,
1444+
})
1445+
}
1446+
13821447
#[inline]
13831448
fn starts_with<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool {
13841449
pat.is_prefix_of(self)
@@ -1527,6 +1592,20 @@ impl StrExt for str {
15271592
}
15281593
}
15291594

1595+
fn split_at_mut(&mut self, mid: usize) -> (&mut str, &mut str) {
1596+
// is_char_boundary checks that the index is in [0, .len()]
1597+
if self.is_char_boundary(mid) {
1598+
let len = self.len();
1599+
unsafe {
1600+
let self2: &mut str = mem::transmute_copy(&self);
1601+
(self.slice_mut_unchecked(0, mid),
1602+
self2.slice_mut_unchecked(mid, len))
1603+
}
1604+
} else {
1605+
slice_error_fail(self, 0, mid)
1606+
}
1607+
}
1608+
15301609
#[inline]
15311610
fn slice_shift_char(&self) -> Option<(char, &str)> {
15321611
if self.is_empty() {

src/libstd/ascii.rs

+60-8
Original file line numberDiff line numberDiff line change
@@ -469,16 +469,19 @@ mod tests {
469469
use char::from_u32;
470470

471471
#[test]
472-
fn test_ascii() {
473-
assert!("banana".chars().all(|c| c.is_ascii()));
474-
assert!(!"ประเทศไทย中华Việt Nam".chars().all(|c| c.is_ascii()));
475-
}
472+
fn test_is_ascii() {
473+
assert!(b"".is_ascii());
474+
assert!(b"banana\0\x7F".is_ascii());
475+
assert!(b"banana\0\x7F".iter().all(|b| b.is_ascii()));
476+
assert!(!b"Vi\xe1\xbb\x87t Nam".is_ascii());
477+
assert!(!b"Vi\xe1\xbb\x87t Nam".iter().all(|b| b.is_ascii()));
478+
assert!(!b"\xe1\xbb\x87".iter().any(|b| b.is_ascii()));
476479

477-
#[test]
478-
fn test_ascii_vec() {
479480
assert!("".is_ascii());
480-
assert!("a".is_ascii());
481-
assert!(!"\u{2009}".is_ascii());
481+
assert!("banana\0\u{7F}".is_ascii());
482+
assert!("banana\0\u{7F}".chars().all(|c| c.is_ascii()));
483+
assert!(!"ประเทศไทย中华Việt Nam".chars().all(|c| c.is_ascii()));
484+
assert!(!"ประเทศไทย中华ệ ".chars().any(|c| c.is_ascii()));
482485
}
483486

484487
#[test]
@@ -537,6 +540,55 @@ mod tests {
537540
}
538541
}
539542

543+
#[test]
544+
fn test_make_ascii_lower_case() {
545+
macro_rules! test {
546+
($from: expr, $to: expr) => {
547+
{
548+
let mut x = $from;
549+
x.make_ascii_lowercase();
550+
assert_eq!(x, $to);
551+
}
552+
}
553+
}
554+
test!(b'A', b'a');
555+
test!(b'a', b'a');
556+
test!(b'!', b'!');
557+
test!('A', 'a');
558+
test!('À', 'À');
559+
test!('a', 'a');
560+
test!('!', '!');
561+
test!(b"H\xc3\x89".to_vec(), b"h\xc3\x89");
562+
test!("HİKß".to_string(), "hİKß");
563+
}
564+
565+
566+
#[test]
567+
fn test_make_ascii_upper_case() {
568+
macro_rules! test {
569+
($from: expr, $to: expr) => {
570+
{
571+
let mut x = $from;
572+
x.make_ascii_uppercase();
573+
assert_eq!(x, $to);
574+
}
575+
}
576+
}
577+
test!(b'a', b'A');
578+
test!(b'A', b'A');
579+
test!(b'!', b'!');
580+
test!('a', 'A');
581+
test!('à', 'à');
582+
test!('A', 'A');
583+
test!('!', '!');
584+
test!(b"h\xc3\xa9".to_vec(), b"H\xc3\xa9");
585+
test!("hıKß".to_string(), "HıKß");
586+
587+
let mut x = "Hello".to_string();
588+
x[..3].make_ascii_uppercase(); // Test IndexMut on String.
589+
assert_eq!(x, "HELlo")
590+
}
591+
540592
#[test]
541593
fn test_eq_ignore_ascii_case() {
542594
assert!("url()URL()uRl()Ürl".eq_ignore_ascii_case("url()url()url()Ürl"));

src/test/compile-fail/str-mut-idx-2.rs

-16
This file was deleted.

src/test/compile-fail/str-mut-idx.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@ fn bot<T>() -> T { loop {} }
1313
fn mutate(s: &mut str) {
1414
s[1..2] = bot();
1515
//~^ ERROR `core::marker::Sized` is not implemented for the type `str`
16-
//~^^ ERROR `core::marker::Sized` is not implemented for the type `str`
16+
//~| ERROR `core::marker::Sized` is not implemented for the type `str`
1717
s[1usize] = bot();
1818
//~^ ERROR `core::ops::Index<usize>` is not implemented for the type `str`
19-
//~^^ ERROR `core::ops::Index<usize>` is not implemented for the type `str`
19+
//~| ERROR `core::ops::IndexMut<usize>` is not implemented for the type `str`
20+
//~| ERROR `core::ops::Index<usize>` is not implemented for the type `str`
2021
}
2122

2223
pub fn main() {}

0 commit comments

Comments
 (0)