I would write more effective version than suggested above. My version has O(n) complexity. It doesn't allocate if string is ASCII but still need it if string is unicode.
There are possibility for other improvements though, for example, you can rid allocation if all chars in string have same length in utf8 form (but don't forget about alignment doing this).
Also, I made function accept &mut str
because it is better since it allows wider range of input (e.g. reverse only substring).
You can see how much things need to be considered when working with unicode in unsafe Rust in comments.
fn reverse_str(s: &mut str){
fn reverse_slice<T: Copy>(slice: &mut [T]){
let slice_len = slice.len();
if slice_len < 2{
return;
}
slice.swap(0, slice_len-1);
reverse_slice(&mut slice[1..slice_len-1]);
}
if s.is_ascii(){
// Simple case: can reverse inplace
unsafe{
// Safety: string is ASCII
reverse_slice(s.as_bytes_mut());
}
}
else{
// complex case: we need to work with unicode
// Need to allocate, unfortunately
let mut chars: Vec<char> = s.chars().collect();
reverse_slice(&mut chars);
unsafe {
// Safety: We write same chars -> we have same length
// Safety: We write correct UTF8 symbol by symbol
// Safety: There are not possible panics in this unsafe block
let mut bytes = s.as_bytes_mut();
for c in chars{
let bytes_written = c.encode_utf8(bytes).len();
bytes = &mut bytes[bytes_written..]
}
}
}
}
fn main(){
// ASCII
let mut s = "Hello".to_string();
reverse_str(&mut s);
println!("{}", s);
// Unicode
let mut s = "Авокадо 126".to_string();
reverse_str(&mut s);
println!("{}", s);
// Substring
let mut s = "Hello world".to_string();
reverse_str(&mut s[6..]);
println!("{}", s);
}
Output:
olleH
621 одаковА
Hello dlrow
Also, LLVM successfully tail-optimizes this recursion:
https://rust.godbolt.org/z/95sqfM