简体   繁体   中英

How do I change characters at a specific index within a string in rust?

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. Eg 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);
}

( Playground )

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM