about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2015-07-13 23:47:06 +0000
committerbors <bors@rust-lang.org>2015-07-13 23:47:06 +0000
commitdd46cf8b22c39a74b786361e00af22314a32b196 (patch)
treeef42015a6a4e59fe77648d41ca5a79f4e0cdb21f /src
parent72483f58e355c6ec9016cbaba4d9b8d3adbd3867 (diff)
parent3226858e500fa70b46c18d1accedc60060f2bbc0 (diff)
downloadrust-dd46cf8b22c39a74b786361e00af22314a32b196.tar.gz
rust-dd46cf8b22c39a74b786361e00af22314a32b196.zip
Auto merge of #26241 - SimonSapin:derefmut-for-string, r=alexcrichton
See https://github.com/rust-lang/rfcs/issues/1157
Diffstat (limited to 'src')
-rw-r--r--src/libcollections/str.rs18
-rw-r--r--src/libcollections/string.rs40
-rw-r--r--src/libcollectionstest/lib.rs2
-rw-r--r--src/libcollectionstest/str.rs12
-rw-r--r--src/libcore/str/mod.rs79
-rw-r--r--src/libstd/ascii.rs68
-rw-r--r--src/test/compile-fail/str-mut-idx-2.rs16
-rw-r--r--src/test/compile-fail/str-mut-idx.rs5
8 files changed, 213 insertions, 27 deletions
diff --git a/src/libcollections/str.rs b/src/libcollections/str.rs
index f92db5353b3..cb6613998b4 100644
--- a/src/libcollections/str.rs
+++ b/src/libcollections/str.rs
@@ -550,6 +550,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
@@ -776,7 +784,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);
@@ -785,10 +793,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
diff --git a/src/libcollections/string.rs b/src/libcollections/string.rs
index ef1d9b08b4a..91142afeda3 100644
--- a/src/libcollections/string.rs
+++ b/src/libcollections/string.rs
@@ -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;
@@ -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",
diff --git a/src/libcollectionstest/lib.rs b/src/libcollectionstest/lib.rs
index 6bf7380fdeb..d161dc7a287 100644
--- a/src/libcollectionstest/lib.rs
+++ b/src/libcollectionstest/lib.rs
@@ -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)]
@@ -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)]
diff --git a/src/libcollectionstest/str.rs b/src/libcollectionstest/str.rs
index 1c038c29fdf..5419c43ba09 100644
--- a/src/libcollectionstest/str.rs
+++ b/src/libcollectionstest/str.rs
@@ -702,6 +702,18 @@ fn test_split_at() {
 }
 
 #[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() {
     let s = "ศไทย中华Việt Nam";
diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs
index bd6e1a4063a..7e4c2ba3be8 100644
--- a/src/libcore/str/mod.rs
+++ b/src/libcore/str/mod.rs
@@ -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`.
     ///
@@ -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()]`.
@@ -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;
@@ -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
@@ -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>;
@@ -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;
@@ -1380,6 +1437,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)
     }
@@ -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() {
diff --git a/src/libstd/ascii.rs b/src/libstd/ascii.rs
index 9b94b7f7003..cf78fa7b69a 100644
--- a/src/libstd/ascii.rs
+++ b/src/libstd/ascii.rs
@@ -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]
@@ -538,6 +541,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"));
         assert!(!"Ürl".eq_ignore_ascii_case("ürl"));
diff --git a/src/test/compile-fail/str-mut-idx-2.rs b/src/test/compile-fail/str-mut-idx-2.rs
deleted file mode 100644
index aa05d7d5c54..00000000000
--- a/src/test/compile-fail/str-mut-idx-2.rs
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-fn mutate(mut s: &mut str) {
-    let _s: &mut str = &mut s[1..2];
-    //~^ ERROR cannot borrow immutable indexed content as mutable
-}
-
-pub fn main() {}
diff --git a/src/test/compile-fail/str-mut-idx.rs b/src/test/compile-fail/str-mut-idx.rs
index 88e90c8244d..73abe6cb59d 100644
--- a/src/test/compile-fail/str-mut-idx.rs
+++ b/src/test/compile-fail/str-mut-idx.rs
@@ -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() {}