diff --git a/library/alloctests/tests/lib.rs b/library/alloctests/tests/lib.rs index 5e80ea2a78843..7ccc8d9c6a115 100644 --- a/library/alloctests/tests/lib.rs +++ b/library/alloctests/tests/lib.rs @@ -33,6 +33,7 @@ #![feature(slice_ptr_get)] #![feature(slice_range)] #![feature(str_as_str)] +#![feature(str_copy_from_str)] #![feature(strict_provenance_lints)] #![feature(string_remove_matches)] #![feature(string_replace_in_place)] diff --git a/library/alloctests/tests/str.rs b/library/alloctests/tests/str.rs index 9f39f578935ad..830f6972f5af5 100644 --- a/library/alloctests/tests/str.rs +++ b/library/alloctests/tests/str.rs @@ -1108,6 +1108,21 @@ fn test_split_at_boundscheck() { let _ = s.split_at(1); } +#[test] +fn test_copy_from_str() { + let src = "Saludos"; + let mut dst = "Grüße, Jürgen".to_string(); + dst[..7].copy_from_str(src); + assert_eq!(dst, "Saludos, Jürgen"); +} + +#[test] +#[should_panic] +fn test_copy_from_str_unequal_len() { + let mut dst = "hello".to_string(); + dst.copy_from_str("hi"); +} + #[test] fn test_escape_unicode() { assert_eq!("abc".escape_unicode().to_string(), "\\u{61}\\u{62}\\u{63}"); diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index c465e3cb0a230..206b32401ceef 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -2969,6 +2969,69 @@ impl str { me.make_ascii_lowercase() } + /// Copies the string from `src` into `self`, using a memcpy. + /// + /// The length of `src` must be the same as `self`. + /// + /// # Panics + /// + /// This function will panic if the two strings have different lengths. + /// + /// # Examples + /// + /// ``` + /// #![feature(str_copy_from_str)] + /// let src = "Saludos"; + /// let mut dst = String::from("Grüße, Jürgen"); + /// + /// // Because the strings have to be the same length, + /// // we slice the destination slice from sixteen bytes + /// // to seven. It will panic if we don't do this. + /// dst[..7].copy_from_str(src); + /// + /// assert_eq!(src, "Saludos"); + /// assert_eq!(dst, "Saludos, Jürgen"); + /// ``` + /// + /// Rust enforces that there can only be one mutable reference with no + /// immutable references to a particular piece of data in a particular + /// scope. Because of this, attempting to use `copy_from_str` on a + /// single string will result in a compile failure: + /// + /// ```compile_fail + /// #![feature(str_copy_from_str)] + /// let mut string = String::from("Abcde"); + /// + /// string[..2].copy_from_str(&string[3..]); // compile fail! + /// ``` + /// + /// To work around this, we can use [`split_at_mut`] to create two distinct + /// sub-slices from a string: + /// + /// ``` + /// #![feature(str_copy_from_str)] + /// let mut string = String::from("Abcde"); + /// + /// { + /// let (left, right) = string.split_at_mut(2); + /// left.copy_from_str(&right[1..]); + /// } + /// + /// assert_eq!(string, "decde"); + /// ``` + /// + /// [`split_at_mut`]: str::split_at_mut + #[doc(alias = "memcpy")] + #[inline] + #[unstable(feature = "str_copy_from_str", issue = "159841")] + #[track_caller] + pub fn copy_from_str(&mut self, src: &str) { + // SAFETY: `copy_from_slice` panics unless the lengths are equal, and copying same-length + // UTF-8 into a `str` keeps it valid UTF-8. + let me = unsafe { self.as_bytes_mut() }; + me.copy_from_slice(src.as_bytes()); + } + /// Returns a string slice with leading ASCII whitespace removed. /// /// 'Whitespace' refers to the definition used by