Auto merge of #26241 - SimonSapin:derefmut-for-string, r=alexcrichton
See https://github.com/rust-lang/rfcs/issues/1157
This commit is contained in:
@@ -550,6 +550,14 @@ impl str {
|
|||||||
core_str::StrExt::slice_unchecked(self, begin, end)
|
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`).
|
/// 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
|
/// That is, start at the `begin`-th code point of the string and continue
|
||||||
@@ -776,7 +784,7 @@ impl str {
|
|||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// ```
|
/// ```
|
||||||
/// # #![feature(collections)]
|
/// # #![feature(str_split_at)]
|
||||||
/// let s = "Löwe 老虎 Léopard";
|
/// let s = "Löwe 老虎 Léopard";
|
||||||
/// let first_space = s.find(' ').unwrap_or(s.len());
|
/// let first_space = s.find(' ').unwrap_or(s.len());
|
||||||
/// let (a, b) = s.split_at(first_space);
|
/// let (a, b) = s.split_at(first_space);
|
||||||
@@ -785,10 +793,18 @@ impl str {
|
|||||||
/// assert_eq!(b, " 老虎 Léopard");
|
/// assert_eq!(b, " 老虎 Léopard");
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
|
#[unstable(feature = "str_split_at", reason = "recently added")]
|
||||||
pub fn split_at(&self, mid: usize) -> (&str, &str) {
|
pub fn split_at(&self, mid: usize) -> (&str, &str) {
|
||||||
core_str::StrExt::split_at(self, mid)
|
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`.
|
/// An iterator over the codepoints of `self`.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
|
|||||||
@@ -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")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
impl ops::Deref for String {
|
impl ops::Deref for String {
|
||||||
type Target = str;
|
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`.
|
/// Wrapper type providing a `&String` reference via `Deref`.
|
||||||
#[unstable(feature = "collections")]
|
#[unstable(feature = "collections")]
|
||||||
#[deprecated(since = "1.2.0",
|
#[deprecated(since = "1.2.0",
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
|
#![feature(ascii)]
|
||||||
#![feature(append)]
|
#![feature(append)]
|
||||||
#![feature(bitset)]
|
#![feature(bitset)]
|
||||||
#![feature(bitvec)]
|
#![feature(bitvec)]
|
||||||
@@ -43,6 +44,7 @@
|
|||||||
#![feature(str_char)]
|
#![feature(str_char)]
|
||||||
#![feature(str_escape)]
|
#![feature(str_escape)]
|
||||||
#![feature(str_match_indices)]
|
#![feature(str_match_indices)]
|
||||||
|
#![feature(str_split_at)]
|
||||||
#![feature(str_utf16)]
|
#![feature(str_utf16)]
|
||||||
#![feature(box_str)]
|
#![feature(box_str)]
|
||||||
#![feature(subslice_offset)]
|
#![feature(subslice_offset)]
|
||||||
|
|||||||
@@ -701,6 +701,18 @@ fn test_split_at() {
|
|||||||
assert_eq!(b, "");
|
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]
|
#[test]
|
||||||
#[should_panic]
|
#[should_panic]
|
||||||
fn test_split_at_boundscheck() {
|
fn test_split_at_boundscheck() {
|
||||||
|
|||||||
@@ -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
|
/// Returns a slice of the string from the beginning to byte
|
||||||
/// `end`.
|
/// `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.
|
/// Returns a slice of the string from `begin` to its end.
|
||||||
///
|
///
|
||||||
/// Equivalent to `self[begin .. self.len()]`.
|
/// 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")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
impl ops::Index<ops::RangeFull> for str {
|
impl ops::Index<ops::RangeFull> for str {
|
||||||
type Output = str;
|
type Output = str;
|
||||||
@@ -1168,6 +1215,14 @@ mod traits {
|
|||||||
self
|
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
|
/// Methods for string slices
|
||||||
@@ -1204,6 +1259,7 @@ pub trait StrExt {
|
|||||||
fn char_len(&self) -> usize;
|
fn char_len(&self) -> usize;
|
||||||
fn slice_chars<'a>(&'a self, begin: usize, end: usize) -> &'a str;
|
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_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 starts_with<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool;
|
||||||
fn ends_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>;
|
where P::Searcher: ReverseSearcher<'a>;
|
||||||
@@ -1223,6 +1279,7 @@ pub trait StrExt {
|
|||||||
where P::Searcher: ReverseSearcher<'a>;
|
where P::Searcher: ReverseSearcher<'a>;
|
||||||
fn find_str<'a, P: Pattern<'a>>(&'a self, pat: P) -> Option<usize>;
|
fn find_str<'a, P: Pattern<'a>>(&'a self, pat: P) -> Option<usize>;
|
||||||
fn split_at(&self, mid: usize) -> (&str, &str);
|
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 slice_shift_char<'a>(&'a self) -> Option<(char, &'a str)>;
|
||||||
fn subslice_offset(&self, inner: &str) -> usize;
|
fn subslice_offset(&self, inner: &str) -> usize;
|
||||||
fn as_ptr(&self) -> *const u8;
|
fn as_ptr(&self) -> *const u8;
|
||||||
@@ -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]
|
#[inline]
|
||||||
fn starts_with<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool {
|
fn starts_with<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool {
|
||||||
pat.is_prefix_of(self)
|
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]
|
#[inline]
|
||||||
fn slice_shift_char(&self) -> Option<(char, &str)> {
|
fn slice_shift_char(&self) -> Option<(char, &str)> {
|
||||||
if self.is_empty() {
|
if self.is_empty() {
|
||||||
|
|||||||
@@ -469,16 +469,19 @@ mod tests {
|
|||||||
use char::from_u32;
|
use char::from_u32;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_ascii() {
|
fn test_is_ascii() {
|
||||||
assert!("banana".chars().all(|c| c.is_ascii()));
|
assert!(b"".is_ascii());
|
||||||
assert!(!"ประเทศไทย中华Việt Nam".chars().all(|c| c.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!("".is_ascii());
|
||||||
assert!("a".is_ascii());
|
assert!("banana\0\u{7F}".is_ascii());
|
||||||
assert!(!"\u{2009}".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]
|
#[test]
|
||||||
@@ -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]
|
#[test]
|
||||||
fn test_eq_ignore_ascii_case() {
|
fn test_eq_ignore_ascii_case() {
|
||||||
assert!("url()URL()uRl()Ürl".eq_ignore_ascii_case("url()url()url()Ürl"));
|
assert!("url()URL()uRl()Ürl".eq_ignore_ascii_case("url()url()url()Ürl"));
|
||||||
|
|||||||
@@ -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() {}
|
|
||||||
@@ -13,10 +13,11 @@ fn bot<T>() -> T { loop {} }
|
|||||||
fn mutate(s: &mut str) {
|
fn mutate(s: &mut str) {
|
||||||
s[1..2] = bot();
|
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`
|
//~| ERROR `core::marker::Sized` is not implemented for the type `str`
|
||||||
s[1usize] = bot();
|
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::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() {}
|
pub fn main() {}
|
||||||
|
|||||||
Reference in New Issue
Block a user