I am trying to change a single character at a specific index in a string, but I do not know how to in rust. For example, how would I change the 4th character in "hello world" to 'x', so that it would be "helxo world"?
I am trying to change a single character at a specific index in a string, but I do not know how to in rust. For example, how would I change the 4th character in "hello world" to 'x', so that it would be "helxo world"?
The easiest way is to use the replace_range() method like this:
let mut hello = String::from("hello world");
hello.replace_range(3..4,"x");
println!("hello: {}", hello);
Output: hello: helxo world (Playground)
Please note that this will panic if the range to be replaced does not start and end on UTF-8 codepoint boundaries. E.g. this will panic:
let mut hello2 = String::from("hell world");
hello2.replace_range(4..5,"x"); // panics because needs more than one byte in UTF-8
If you want to replace the nth UTF-8 code point, you have to do something like this:
pub fn main() {
let mut hello = String::from("hell world");
hello.replace_range(
hello
.char_indices()
.nth(4)
.map(|(pos, ch)| (pos..pos + ch.len_utf8()))
.unwrap(),
"x",
);
println!("hello: {}", hello);
}
The standard way of representing a string in Rust is as a contiguous range of bytes encoded as a UTF-8 string. UTF-8 codepoints can be from one to 4 bytes long, so generally you can't simply replace one UTF-8 codepoint with another because the length might change. You also can't do simple pointer arithmetic to index into a Rust String to the nth character, because again codepoint encodings can be from 1 to 4 bytes long.
So one safe but slow way to do it would be like this, iterating through the characters of the source string, replacing the one you want, then creating a new string:
fn replace_nth_char(s: &str, idx: usize, newchar: char) -> String {
s.chars().enumerate().map(|(i,c)| if i == idx { newchar } else { c }).collect()
}
But we can do it in O(1) if we manually make sure the old and new character are single-byte ascii.
fn replace_nth_char_safe(s: &str, idx: usize, newchar: char) -> String {
s.chars().enumerate().map(|(i,c)| if i == idx { newchar } else { c }).collect()
}
fn replace_nth_char_ascii(s: &mut str, idx: usize, newchar: char) {
let s_bytes: &mut [u8] = unsafe { s.as_bytes_mut() };
assert!(idx < s_bytes.len());
assert!(s_bytes[idx].is_ascii());
assert!(newchar.is_ascii());
// we've made sure this is safe.
s_bytes[idx] = newchar as u8;
}
fn main() {
let s = replace_nth_char_safe("Hello, world!", 3, 'x');
assert_eq!(s, "Helxo, world!");
let mut s = String::from("Hello, world!");
replace_nth_char_ascii(&mut s, 3, 'x');
assert_eq!(s, "Helxo, world!");
}
Keep in mind that idx parameter in replace_nth_char_ascii is not a character index, but instead a byte index. If there are any multibyte characters earlier in the string, then the byte index and the character index will not correspond.