diff --git a/src/doc/unstable-book/src/SUMMARY.md b/src/doc/unstable-book/src/SUMMARY.md index d86766bac02a2..c077568d96e1b 100644 --- a/src/doc/unstable-book/src/SUMMARY.md +++ b/src/doc/unstable-book/src/SUMMARY.md @@ -187,6 +187,7 @@ - [str_checked_slicing](str-checked-slicing.md) - [str_escape](str-escape.md) - [str_internals](str-internals.md) +- [str_mut_extras](str-mut-extras.md) - [struct_field_attributes](struct-field-attributes.md) - [structural_match](structural-match.md) - [target_feature](target-feature.md) diff --git a/src/doc/unstable-book/src/str-mut-extras.md b/src/doc/unstable-book/src/str-mut-extras.md new file mode 100644 index 0000000000000..df4f35832cdc1 --- /dev/null +++ b/src/doc/unstable-book/src/str-mut-extras.md @@ -0,0 +1,8 @@ +# `str_mut_extras` + +The tracking issue for this feature is: [#str_mut_extras] + +[#str_mut_extras]: https://github.com/rust-lang/rust/issues/41119 + +------------------------ + diff --git a/src/libcollections/lib.rs b/src/libcollections/lib.rs index 248c15e96f8f6..b485f900094f0 100644 --- a/src/libcollections/lib.rs +++ b/src/libcollections/lib.rs @@ -57,6 +57,7 @@ #![feature(specialization)] #![feature(staged_api)] #![feature(str_internals)] +#![feature(str_mut_extras)] #![feature(trusted_len)] #![feature(unicode)] #![feature(unique)] diff --git a/src/libcollections/str.rs b/src/libcollections/str.rs index c37a4fa6b5572..d04f414250ade 100644 --- a/src/libcollections/str.rs +++ b/src/libcollections/str.rs @@ -72,7 +72,7 @@ pub use core::str::{MatchIndices, RMatchIndices}; #[stable(feature = "rust1", since = "1.0.0")] pub use core::str::{from_utf8, Chars, CharIndices, Bytes}; #[stable(feature = "rust1", since = "1.0.0")] -pub use core::str::{from_utf8_unchecked, ParseBoolError}; +pub use core::str::{from_utf8_unchecked, from_utf8_unchecked_mut, ParseBoolError}; #[stable(feature = "rust1", since = "1.0.0")] pub use std_unicode::str::SplitWhitespace; #[stable(feature = "rust1", since = "1.0.0")] @@ -294,6 +294,13 @@ impl str { core_str::StrExt::as_bytes(self) } + /// Converts a mutable string slice to a mutable byte slice. + #[unstable(feature = "str_mut_extras", issue = "41119")] + #[inline(always)] + pub unsafe fn as_bytes_mut(&mut self) -> &mut [u8] { + core_str::StrExt::as_bytes_mut(self) + } + /// Converts a string slice to a raw pointer. /// /// As string slices are a slice of bytes, the raw pointer points to a diff --git a/src/libcollections/string.rs b/src/libcollections/string.rs index 0ee4c8b8e95a6..7d9d7276201bd 100644 --- a/src/libcollections/string.rs +++ b/src/libcollections/string.rs @@ -1790,7 +1790,7 @@ impl ops::IndexMut> for String { impl ops::IndexMut for String { #[inline] fn index_mut(&mut self, _index: ops::RangeFull) -> &mut str { - unsafe { mem::transmute(&mut *self.vec) } + unsafe { str::from_utf8_unchecked_mut(&mut *self.vec) } } } #[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")] @@ -1822,7 +1822,7 @@ impl ops::Deref for String { impl ops::DerefMut for String { #[inline] fn deref_mut(&mut self) -> &mut str { - unsafe { mem::transmute(&mut *self.vec) } + unsafe { str::from_utf8_unchecked_mut(&mut *self.vec) } } } diff --git a/src/libcore/char.rs b/src/libcore/char.rs index b27c801cf89d5..98268e3813fac 100644 --- a/src/libcore/char.rs +++ b/src/libcore/char.rs @@ -19,6 +19,7 @@ use char_private::is_printable; use convert::TryFrom; use fmt::{self, Write}; use slice; +use str::from_utf8_unchecked_mut; use iter::FusedIterator; use mem::transmute; @@ -448,7 +449,7 @@ impl CharExt for char { code, dst.len()) }; - transmute(slice::from_raw_parts_mut(dst.as_mut_ptr(), len)) + from_utf8_unchecked_mut(dst.get_unchecked_mut(..len)) } } diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index 352cc926994e3..2ceef54ffed6a 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -21,8 +21,8 @@ use char; use convert::TryFrom; use fmt; use iter::{Map, Cloned, FusedIterator}; -use mem; use slice::{self, SliceIndex}; +use mem; pub mod pattern; @@ -300,6 +300,13 @@ pub fn from_utf8(v: &[u8]) -> Result<&str, Utf8Error> { Ok(unsafe { from_utf8_unchecked(v) }) } +/// Converts a mutable slice of bytes to a mutable string slice. +#[unstable(feature = "str_mut_extras", issue = "41119")] +pub fn from_utf8_mut(v: &mut [u8]) -> Result<&mut str, Utf8Error> { + run_utf8_validation(v)?; + Ok(unsafe { from_utf8_unchecked_mut(v) }) +} + /// Forms a str from a pointer and a length. /// /// The `len` argument is the number of bytes in the string. @@ -325,7 +332,7 @@ pub fn from_utf8(v: &[u8]) -> Result<&str, Utf8Error> { /// str is returned. /// unsafe fn from_raw_parts_mut<'a>(p: *mut u8, len: usize) -> &'a mut str { - mem::transmute::<&mut [u8], &mut str>(slice::from_raw_parts_mut(p, len)) + from_utf8_unchecked_mut(slice::from_raw_parts_mut(p, len)) } /// Converts a slice of bytes to a string slice without checking @@ -365,6 +372,18 @@ pub unsafe fn from_utf8_unchecked(v: &[u8]) -> &str { mem::transmute(v) } +/// Converts a slice of bytes to a string slice without checking +/// that the string contains valid UTF-8; mutable version. +/// +/// See the immutable version, [`from_utf8_unchecked()`][fromutf8], for more information. +/// +/// [fromutf8]: fn.from_utf8_unchecked.html +#[inline(always)] +#[unstable(feature = "str_mut_extras", issue = "41119")] +pub unsafe fn from_utf8_unchecked_mut(v: &mut [u8]) -> &mut str { + mem::transmute(v) +} + #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Display for Utf8Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -1474,7 +1493,6 @@ Section: Trait implementations mod traits { use cmp::Ordering; use ops; - use mem; use slice::{self, SliceIndex}; use str::eq_slice; @@ -1811,7 +1829,7 @@ mod traits { unsafe fn get_unchecked_mut(self, slice: &mut str) -> &mut Self::Output { let ptr = slice.as_ptr().offset(self.start as isize); let len = self.end - self.start; - mem::transmute(slice::from_raw_parts_mut(ptr as *mut u8, len)) + super::from_utf8_unchecked_mut(slice::from_raw_parts_mut(ptr as *mut u8, len)) } #[inline] fn index(self, slice: &str) -> &Self::Output { @@ -1859,7 +1877,7 @@ mod traits { #[inline] unsafe fn get_unchecked_mut(self, slice: &mut str) -> &mut Self::Output { let ptr = slice.as_ptr(); - mem::transmute(slice::from_raw_parts_mut(ptr as *mut u8, self.end)) + super::from_utf8_unchecked_mut(slice::from_raw_parts_mut(ptr as *mut u8, self.end)) } #[inline] fn index(self, slice: &str) -> &Self::Output { @@ -1905,7 +1923,7 @@ mod traits { unsafe fn get_unchecked_mut(self, slice: &mut str) -> &mut Self::Output { let ptr = slice.as_ptr().offset(self.start as isize); let len = slice.len() - self.start; - mem::transmute(slice::from_raw_parts_mut(ptr as *mut u8, len)) + super::from_utf8_unchecked_mut(slice::from_raw_parts_mut(ptr as *mut u8, len)) } #[inline] fn index(self, slice: &str) -> &Self::Output { @@ -1998,7 +2016,7 @@ mod traits { #[inline] unsafe fn get_unchecked_mut(self, slice: &mut str) -> &mut Self::Output { let ptr = slice.as_ptr(); - mem::transmute(slice::from_raw_parts_mut(ptr as *mut u8, self.end + 1)) + super::from_utf8_unchecked_mut(slice::from_raw_parts_mut(ptr as *mut u8, self.end + 1)) } #[inline] fn index(self, slice: &str) -> &Self::Output { @@ -2096,6 +2114,8 @@ pub trait StrExt { fn is_char_boundary(&self, index: usize) -> bool; #[stable(feature = "core", since = "1.6.0")] fn as_bytes(&self) -> &[u8]; + #[unstable(feature = "str_mut_extras", issue = "0")] + unsafe fn as_bytes_mut(&mut self) -> &mut [u8]; #[stable(feature = "core", since = "1.6.0")] fn find<'a, P: Pattern<'a>>(&'a self, pat: P) -> Option; #[stable(feature = "core", since = "1.6.0")] @@ -2373,6 +2393,11 @@ impl StrExt for str { unsafe { mem::transmute(self) } } + #[inline] + unsafe fn as_bytes_mut(&mut self) -> &mut [u8] { + mem::transmute(self) + } + fn find<'a, P: Pattern<'a>>(&'a self, pat: P) -> Option { pat.into_searcher(self).next_match().map(|(i, _)| i) } diff --git a/src/libstd/ascii.rs b/src/libstd/ascii.rs index b36253862094f..4e3781ecafab5 100644 --- a/src/libstd/ascii.rs +++ b/src/libstd/ascii.rs @@ -27,7 +27,6 @@ #![stable(feature = "rust1", since = "1.0.0")] use fmt; -use mem; use ops::Range; use iter::FusedIterator; @@ -599,12 +598,12 @@ impl AsciiExt for str { } fn make_ascii_uppercase(&mut self) { - let me: &mut [u8] = unsafe { mem::transmute(self) }; + let me = unsafe { self.as_bytes_mut() }; me.make_ascii_uppercase() } fn make_ascii_lowercase(&mut self) { - let me: &mut [u8] = unsafe { mem::transmute(self) }; + let me = unsafe { self.as_bytes_mut() }; me.make_ascii_lowercase() } diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 064144dcd6818..6299a9070ae03 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -296,6 +296,7 @@ #![feature(stmt_expr_attributes)] #![feature(str_char)] #![feature(str_internals)] +#![feature(str_mut_extras)] #![feature(str_utf16)] #![feature(test, rustc_private)] #![feature(thread_local)]