Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement DerefMut for String #26241

Merged
merged 7 commits into from
Jul 14, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion src/libcollections/str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,14 @@ impl str {
core_str::StrExt::slice_unchecked(self, begin, end)
}

/// Takes a bytewise mutable slice from a string.
///
/// Same as `slice_unchecked`, but works with `&mut str` instead of `&str`.
#[unstable(feature = "str_slice_mut", reason = "recently added")]
pub unsafe fn slice_mut_unchecked(&mut self, begin: usize, end: usize) -> &mut str {
core_str::StrExt::slice_mut_unchecked(self, begin, end)
}

/// Returns a slice of the string from the character range [`begin`..`end`).
///
/// That is, start at the `begin`-th code point of the string and continue
Expand Down Expand Up @@ -772,7 +780,7 @@ impl str {
///
/// # Examples
/// ```
/// # #![feature(collections)]
/// # #![feature(str_split_at)]
/// let s = "Löwe 老虎 Léopard";
/// let first_space = s.find(' ').unwrap_or(s.len());
/// let (a, b) = s.split_at(first_space);
Expand All @@ -781,10 +789,18 @@ impl str {
/// assert_eq!(b, " 老虎 Léopard");
/// ```
#[inline]
#[unstable(feature = "str_split_at", reason = "recently added")]
pub fn split_at(&self, mid: usize) -> (&str, &str) {
core_str::StrExt::split_at(self, mid)
}

/// Divide one mutable string slice into two at an index.
#[inline]
#[unstable(feature = "str_split_at", reason = "recently added")]
pub fn split_at_mut(&mut self, mid: usize) -> (&mut str, &mut str) {
core_str::StrExt::split_at_mut(self, mid)
}

/// An iterator over the codepoints of `self`.
///
/// # Examples
Expand Down
40 changes: 40 additions & 0 deletions src/libcollections/string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -979,6 +979,38 @@ impl ops::Index<ops::RangeFull> for String {
}
}

#[cfg(not(stage0))]
#[stable(feature = "derefmut_for_string", since = "1.2.0")]
impl ops::IndexMut<ops::Range<usize>> for String {
#[inline]
fn index_mut(&mut self, index: ops::Range<usize>) -> &mut str {
&mut self[..][index]
}
}
#[cfg(not(stage0))]
#[stable(feature = "derefmut_for_string", since = "1.2.0")]
impl ops::IndexMut<ops::RangeTo<usize>> for String {
#[inline]
fn index_mut(&mut self, index: ops::RangeTo<usize>) -> &mut str {
&mut self[..][index]
}
}
#[cfg(not(stage0))]
#[stable(feature = "derefmut_for_string", since = "1.2.0")]
impl ops::IndexMut<ops::RangeFrom<usize>> for String {
#[inline]
fn index_mut(&mut self, index: ops::RangeFrom<usize>) -> &mut str {
&mut self[..][index]
}
}
#[stable(feature = "derefmut_for_string", since = "1.2.0")]
impl ops::IndexMut<ops::RangeFull> for String {
#[inline]
fn index_mut(&mut self, _index: ops::RangeFull) -> &mut str {
unsafe { mem::transmute(&mut *self.vec) }
}
}

#[stable(feature = "rust1", since = "1.0.0")]
impl ops::Deref for String {
type Target = str;
Expand All @@ -989,6 +1021,14 @@ impl ops::Deref for String {
}
}

#[stable(feature = "derefmut_for_string", since = "1.2.0")]
impl ops::DerefMut for String {
#[inline]
fn deref_mut(&mut self) -> &mut str {
unsafe { mem::transmute(&mut self.vec[..]) }
}
}

/// Wrapper type providing a `&String` reference via `Deref`.
#[unstable(feature = "collections")]
#[deprecated(since = "1.2.0",
Expand Down
2 changes: 2 additions & 0 deletions src/libcollectionstest/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![feature(ascii)]
#![feature(append)]
#![feature(bitset)]
#![feature(bitvec)]
Expand Down Expand Up @@ -43,6 +44,7 @@
#![feature(str_char)]
#![feature(str_escape)]
#![feature(str_match_indices)]
#![feature(str_split_at)]
#![feature(str_utf16)]
#![feature(box_str)]
#![feature(subslice_offset)]
Expand Down
12 changes: 12 additions & 0 deletions src/libcollectionstest/str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -701,6 +701,18 @@ fn test_split_at() {
assert_eq!(b, "");
}

#[test]
fn test_split_at_mut() {
use std::ascii::AsciiExt;
let mut s = "Hello World".to_string();
{
let (a, b) = s.split_at_mut(5);
a.make_ascii_uppercase();
b.make_ascii_lowercase();
}
assert_eq!(s, "HELLO world");
}

#[test]
#[should_panic]
fn test_split_at_boundscheck() {
Expand Down
79 changes: 79 additions & 0 deletions src/libcore/str/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1116,6 +1116,23 @@ mod traits {
}
}

/// Returns a mutable slice of the given string from the byte range
/// [`begin`..`end`).
#[stable(feature = "derefmut_for_string", since = "1.2.0")]
impl ops::IndexMut<ops::Range<usize>> for str {
#[inline]
fn index_mut(&mut self, index: ops::Range<usize>) -> &mut str {
// is_char_boundary checks that the index is in [0, .len()]
if index.start <= index.end &&
self.is_char_boundary(index.start) &&
self.is_char_boundary(index.end) {
unsafe { self.slice_mut_unchecked(index.start, index.end) }
} else {
super::slice_error_fail(self, index.start, index.end)
}
}
}

/// Returns a slice of the string from the beginning to byte
/// `end`.
///
Expand All @@ -1138,6 +1155,21 @@ mod traits {
}
}

/// Returns a mutable slice of the string from the beginning to byte
/// `end`.
#[stable(feature = "derefmut_for_string", since = "1.2.0")]
impl ops::IndexMut<ops::RangeTo<usize>> for str {
#[inline]
fn index_mut(&mut self, index: ops::RangeTo<usize>) -> &mut str {
// is_char_boundary checks that the index is in [0, .len()]
if self.is_char_boundary(index.end) {
unsafe { self.slice_mut_unchecked(0, index.end) }
} else {
super::slice_error_fail(self, 0, index.end)
}
}
}

/// Returns a slice of the string from `begin` to its end.
///
/// Equivalent to `self[begin .. self.len()]`.
Expand All @@ -1159,6 +1191,21 @@ mod traits {
}
}

/// Returns a slice of the string from `begin` to its end.
#[stable(feature = "derefmut_for_string", since = "1.2.0")]
impl ops::IndexMut<ops::RangeFrom<usize>> for str {
#[inline]
fn index_mut(&mut self, index: ops::RangeFrom<usize>) -> &mut str {
// is_char_boundary checks that the index is in [0, .len()]
if self.is_char_boundary(index.start) {
let len = self.len();
unsafe { self.slice_mut_unchecked(index.start, len) }
} else {
super::slice_error_fail(self, index.start, self.len())
}
}
}

#[stable(feature = "rust1", since = "1.0.0")]
impl ops::Index<ops::RangeFull> for str {
type Output = str;
Expand All @@ -1168,6 +1215,14 @@ mod traits {
self
}
}

#[stable(feature = "derefmut_for_string", since = "1.2.0")]
impl ops::IndexMut<ops::RangeFull> for str {
#[inline]
fn index_mut(&mut self, _index: ops::RangeFull) -> &mut str {
self
}
}
}

/// Methods for string slices
Expand Down Expand Up @@ -1204,6 +1259,7 @@ pub trait StrExt {
fn char_len(&self) -> usize;
fn slice_chars<'a>(&'a self, begin: usize, end: usize) -> &'a str;
unsafe fn slice_unchecked<'a>(&'a self, begin: usize, end: usize) -> &'a str;
unsafe fn slice_mut_unchecked<'a>(&'a mut self, begin: usize, end: usize) -> &'a mut str;
fn starts_with<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool;
fn ends_with<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool
where P::Searcher: ReverseSearcher<'a>;
Expand All @@ -1223,6 +1279,7 @@ pub trait StrExt {
where P::Searcher: ReverseSearcher<'a>;
fn find_str<'a, P: Pattern<'a>>(&'a self, pat: P) -> Option<usize>;
fn split_at(&self, mid: usize) -> (&str, &str);
fn split_at_mut(&mut self, mid: usize) -> (&mut str, &mut str);
fn slice_shift_char<'a>(&'a self) -> Option<(char, &'a str)>;
fn subslice_offset(&self, inner: &str) -> usize;
fn as_ptr(&self) -> *const u8;
Expand Down Expand Up @@ -1379,6 +1436,14 @@ impl StrExt for str {
})
}

#[inline]
unsafe fn slice_mut_unchecked(&mut self, begin: usize, end: usize) -> &mut str {
mem::transmute(Slice {
data: self.as_ptr().offset(begin as isize),
len: end - begin,
})
}

#[inline]
fn starts_with<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool {
pat.is_prefix_of(self)
Expand Down Expand Up @@ -1527,6 +1592,20 @@ impl StrExt for str {
}
}

fn split_at_mut(&mut self, mid: usize) -> (&mut str, &mut str) {
// is_char_boundary checks that the index is in [0, .len()]
if self.is_char_boundary(mid) {
let len = self.len();
unsafe {
let self2: &mut str = mem::transmute_copy(&self);
(self.slice_mut_unchecked(0, mid),
self2.slice_mut_unchecked(mid, len))
}
} else {
slice_error_fail(self, 0, mid)
}
}

#[inline]
fn slice_shift_char(&self) -> Option<(char, &str)> {
if self.is_empty() {
Expand Down
68 changes: 60 additions & 8 deletions src/libstd/ascii.rs
Original file line number Diff line number Diff line change
Expand Up @@ -469,16 +469,19 @@ mod tests {
use char::from_u32;

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

#[test]
fn test_ascii_vec() {
assert!("".is_ascii());
assert!("a".is_ascii());
assert!(!"\u{2009}".is_ascii());
assert!("banana\0\u{7F}".is_ascii());
assert!("banana\0\u{7F}".chars().all(|c| c.is_ascii()));
assert!(!"ประเทศไทย中华Việt Nam".chars().all(|c| c.is_ascii()));
assert!(!"ประเทศไทย中华ệ ".chars().any(|c| c.is_ascii()));
}

#[test]
Expand Down Expand Up @@ -537,6 +540,55 @@ mod tests {
}
}

#[test]
fn test_make_ascii_lower_case() {
macro_rules! test {
($from: expr, $to: expr) => {
{
let mut x = $from;
x.make_ascii_lowercase();
assert_eq!(x, $to);
}
}
}
test!(b'A', b'a');
test!(b'a', b'a');
test!(b'!', b'!');
test!('A', 'a');
test!('À', 'À');
test!('a', 'a');
test!('!', '!');
test!(b"H\xc3\x89".to_vec(), b"h\xc3\x89");
test!("HİKß".to_string(), "hİKß");
}


#[test]
fn test_make_ascii_upper_case() {
macro_rules! test {
($from: expr, $to: expr) => {
{
let mut x = $from;
x.make_ascii_uppercase();
assert_eq!(x, $to);
}
}
}
test!(b'a', b'A');
test!(b'A', b'A');
test!(b'!', b'!');
test!('a', 'A');
test!('à', 'à');
test!('A', 'A');
test!('!', '!');
test!(b"h\xc3\xa9".to_vec(), b"H\xc3\xa9");
test!("hıKß".to_string(), "HıKß");

let mut x = "Hello".to_string();
x[..3].make_ascii_uppercase(); // Test IndexMut on String.
assert_eq!(x, "HELlo")
}

#[test]
fn test_eq_ignore_ascii_case() {
assert!("url()URL()uRl()Ürl".eq_ignore_ascii_case("url()url()url()Ürl"));
Expand Down
16 changes: 0 additions & 16 deletions src/test/compile-fail/str-mut-idx-2.rs

This file was deleted.

5 changes: 3 additions & 2 deletions src/test/compile-fail/str-mut-idx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ fn bot<T>() -> T { loop {} }
fn mutate(s: &mut str) {
s[1..2] = bot();
//~^ ERROR `core::marker::Sized` is not implemented for the type `str`
//~^^ ERROR `core::marker::Sized` is not implemented for the type `str`
//~| ERROR `core::marker::Sized` is not implemented for the type `str`
s[1usize] = bot();
//~^ ERROR `core::ops::Index<usize>` is not implemented for the type `str`
//~^^ ERROR `core::ops::Index<usize>` is not implemented for the type `str`
//~| ERROR `core::ops::IndexMut<usize>` is not implemented for the type `str`
//~| ERROR `core::ops::Index<usize>` is not implemented for the type `str`
}

pub fn main() {}