[英]How to reverse a Rust String in-place using recursion?
當使用遞歸反轉String
時,我發現很難將String
的一部分繼續到下一個,因為 slice 是&str
類型而不是String
。
這不運行:
fn reverse_string(s: &mut String) {
if s.is_empty() {
return;
}
// how to pass a correct parameter?
reverse_string(&mut s[1..]);
s.push(s.chars().nth(0).unwrap());
s.remove(0);
}
error[E0308]: mismatched types
--> src/lib.rs:6:20
|
6 | reverse_string(&mut s[1..]);
| ^^^^^^^^^^^ expected struct `String`, found `str`
|
= note: expected mutable reference `&mut String`
found mutable reference `&mut str`
Rust 字符串為 UTF-8,這意味着
如果您只想交換ASCII 字符串的字符,則可以這樣做:
use std::mem;
fn reverse_string_ascii(s: &mut str) {
if !s.is_ascii() {
return;
}
// Safety: We have checked that the string is ASCII,
// so it's fine to treat it as a slice of bytes.
unsafe {
fn rev(b: &mut [u8]) {
match b {
[] => {}
[_] => {}
[h, rest @ .., t] => {
mem::swap(h, t);
rev(rest)
}
}
}
rev(s.as_bytes_mut());
}
}
fn main() {
let mut s = String::from("hello");
reverse_string_ascii(&mut s);
println!("{}", s);
}
雖然沒有真正的理由在這里使用遞歸,但迭代更好:
let mut todo = s.as_bytes_mut();
loop {
match todo {
[] => break,
[_] => break,
[h, rest @ .., t] => {
mem::swap(h, t);
todo = rest;
}
}
}
也可以看看:
String
數據類型的切片屬於數據類型&str
,因此您的程序無法編譯,編譯器還聲明他期望String
但得到了str
。 您可以嘗試轉換數據類型,但您可能必須以不同的方式編寫程序。
我不確定您為什么要嘗試使用遞歸來做到這一點,但我敢肯定您有這樣做的理由;)
我做了一個工作演示,展示了如何在不使用切片但僅使用String
和char
的情況下天真地做到這一點:
fn rec_rev_str(mut s: String) -> String {
if s.is_empty() {
s
} else {
let removed_char = s.remove(0);
let mut s = rec_rev_str(s);
s.push(removed_char);
s
}
}
fn main() {
let s = String::from("A test String :)");
println!("{}", rec_rev_str(s));
}
我會寫出比上面建議的更有效的版本。 我的版本具有 O(n) 復雜度。 如果字符串是 ASCII,它不會分配,但如果字符串是 unicode,它仍然需要它。
雖然還有其他改進的可能性,例如,如果字符串中的所有字符在 utf8 格式中具有相同的長度,您可以取消分配(但不要忘記 alignment 這樣做)。
另外,我讓 function 接受&mut str
因為它更好,因為它允許更廣泛的輸入(例如,僅反轉子字符串)。
您可以在評論中看到在不安全的 Rust 中使用 unicode 時需要考慮多少事情。
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
此外,LLVM 成功地對該遞歸進行了尾部優化: https://rust.godbolt.org/z/95sqfM
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.