Skip to content

Commit a0be0d7

Browse files
Rollup merge of rust-lang#159846 - valentynkit:str-copy-from-str, r=jhpratt
Implement `str::copy_from_str` Implements the accepted ACP rust-lang/libs-team#709: add `str::copy_from_str`, which copies a same-length `&str` into a mutable `str` in place, mirroring `<[T]>::copy_from_slice`. The write is safe because UTF-8 is self-recovering: overwriting a `str` with a same-length `&str` cannot produce invalid UTF-8, and `copy_from_slice` enforces the length equality (panics otherwise). The ACP accepted `copy_from_str` and rejected `copy_within`; `swap_with_str` was left out pending a use case, so this adds only the one method. Tracking issue: rust-lang#159841 r? libs
2 parents df505a9 + 4cf766b commit a0be0d7

3 files changed

Lines changed: 79 additions & 0 deletions

File tree

library/alloctests/tests/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#![feature(slice_ptr_get)]
3434
#![feature(slice_range)]
3535
#![feature(str_as_str)]
36+
#![feature(str_copy_from_str)]
3637
#![feature(strict_provenance_lints)]
3738
#![feature(string_remove_matches)]
3839
#![feature(string_replace_in_place)]

library/alloctests/tests/str.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1108,6 +1108,21 @@ fn test_split_at_boundscheck() {
11081108
let _ = s.split_at(1);
11091109
}
11101110

1111+
#[test]
1112+
fn test_copy_from_str() {
1113+
let src = "Saludos";
1114+
let mut dst = "Grüße, Jürgen".to_string();
1115+
dst[..7].copy_from_str(src);
1116+
assert_eq!(dst, "Saludos, Jürgen");
1117+
}
1118+
1119+
#[test]
1120+
#[should_panic]
1121+
fn test_copy_from_str_unequal_len() {
1122+
let mut dst = "hello".to_string();
1123+
dst.copy_from_str("hi");
1124+
}
1125+
11111126
#[test]
11121127
fn test_escape_unicode() {
11131128
assert_eq!("abc".escape_unicode().to_string(), "\\u{61}\\u{62}\\u{63}");

library/core/src/str/mod.rs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2969,6 +2969,69 @@ impl str {
29692969
me.make_ascii_lowercase()
29702970
}
29712971

2972+
/// Copies the string from `src` into `self`, using a memcpy.
2973+
///
2974+
/// The length of `src` must be the same as `self`.
2975+
///
2976+
/// # Panics
2977+
///
2978+
/// This function will panic if the two strings have different lengths.
2979+
///
2980+
/// # Examples
2981+
///
2982+
/// ```
2983+
/// #![feature(str_copy_from_str)]
2984+
/// let src = "Saludos";
2985+
/// let mut dst = String::from("Grüße, Jürgen");
2986+
///
2987+
/// // Because the strings have to be the same length,
2988+
/// // we slice the destination slice from sixteen bytes
2989+
/// // to seven. It will panic if we don't do this.
2990+
/// dst[..7].copy_from_str(src);
2991+
///
2992+
/// assert_eq!(src, "Saludos");
2993+
/// assert_eq!(dst, "Saludos, Jürgen");
2994+
/// ```
2995+
///
2996+
/// Rust enforces that there can only be one mutable reference with no
2997+
/// immutable references to a particular piece of data in a particular
2998+
/// scope. Because of this, attempting to use `copy_from_str` on a
2999+
/// single string will result in a compile failure:
3000+
///
3001+
/// ```compile_fail
3002+
/// #![feature(str_copy_from_str)]
3003+
/// let mut string = String::from("Abcde");
3004+
///
3005+
/// string[..2].copy_from_str(&string[3..]); // compile fail!
3006+
/// ```
3007+
///
3008+
/// To work around this, we can use [`split_at_mut`] to create two distinct
3009+
/// sub-slices from a string:
3010+
///
3011+
/// ```
3012+
/// #![feature(str_copy_from_str)]
3013+
/// let mut string = String::from("Abcde");
3014+
///
3015+
/// {
3016+
/// let (left, right) = string.split_at_mut(2);
3017+
/// left.copy_from_str(&right[1..]);
3018+
/// }
3019+
///
3020+
/// assert_eq!(string, "decde");
3021+
/// ```
3022+
///
3023+
/// [`split_at_mut`]: str::split_at_mut
3024+
#[doc(alias = "memcpy")]
3025+
#[inline]
3026+
#[unstable(feature = "str_copy_from_str", issue = "159841")]
3027+
#[track_caller]
3028+
pub fn copy_from_str(&mut self, src: &str) {
3029+
// SAFETY: `copy_from_slice` panics unless the lengths are equal, and copying same-length
3030+
// UTF-8 into a `str` keeps it valid UTF-8.
3031+
let me = unsafe { self.as_bytes_mut() };
3032+
me.copy_from_slice(src.as_bytes());
3033+
}
3034+
29723035
/// Returns a string slice with leading ASCII whitespace removed.
29733036
///
29743037
/// 'Whitespace' refers to the definition used by

0 commit comments

Comments
 (0)