Skip to content

Commit

Permalink
Implement IndexMut for String and str.
Browse files Browse the repository at this point in the history
... matching the existing Index impls.
There is no reason not to if String implement DerefMut.

The code removed in `src/librustc/middle/effect.rs` was added in #9750
to prevent things like `s[0] = 0x80` where `s: String`,
but I belive became unnecessary when the Index(Mut) traits were introduced.
  • Loading branch information
SimonSapin committed Jul 13, 2015
1 parent 90d61d8 commit f900551
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 0 deletions.
8 changes: 8 additions & 0 deletions 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`.
#[stable(feature = "derefmut_for_string", since = "1.2.0")]
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
32 changes: 32 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 Down
64 changes: 64 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 Down Expand Up @@ -1379,6 +1435,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
4 changes: 4 additions & 0 deletions src/libstd/ascii.rs
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,10 @@ mod tests {
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]
Expand Down

0 comments on commit f900551

Please sign in to comment.