Reputation: 2233
I am working my way through the codefights exercises and ended up with some messy code for what, in Python, is relatively simple string slicing and concatenation:
s = "the quick (brown (fox) jumps) over"
(f, t) = (18, 21)
ss = s[:f] + s[t-1:f-1:-1] + s[t:]
In Rust:
let s = String::from("the quick (brown (fox) jumps) over");
let sc: Vec<_> = s.chars().collect();
let (f, t) = (18, 21); // result of searching through sc
let mut new_s: Vec<_> = sc[..f].into_iter().collect();
new_s.extend(&sc[f..t].into_iter().rev().collect::<Vec<_>>());
new_s.extend(&sc[t..].into_iter().collect::<Vec<_>>());
let mut ss = String::with_capacity(new_s.len());
for c in new_s.iter() {ss.push(**c);}
Mainly by adding things to fix compile errors I've ended up with complexity that I just don't seem able to reduce. What is the 'proper' way to do this in Rust?
I didn't do something like the answer using replace()
because this is a simplified example of a problem to search through a string and reverse the parts inside matching brackets.
Upvotes: 0
Views: 935
Reputation: 23339
This should only allocate two times to your 5, I'm not sure if there is a way to get rid of the intermediate allocation:
let ss: String = s.chars()
.take(f)
.chain(s.chars().skip(f).take(t-f).collect::<Vec<_>>().into_iter().rev())
.chain(s.chars().skip(t))
.collect();
Note that UTF-8 string manipulation is a lot more complex than the Python code would seem to imply. For example, both solutions will fail to do what you expect in the presence of combining characters (playground).
The following gets rid of the intermediate allocation:
let ss: String = s.chars()
.take(f)
.chain(s.chars().rev().skip(s.chars().count()-t).take(t-f))
.chain(s.chars().skip(t))
.collect();
We need to use s.chars().count()
instead of s.len()
for Unicode, because s.len()
is in code units and we need the length in code points.
This is easier to understand but only works for ASCII or if f
and t
are indices in code units:
let ss: String = s[..f].chars()
.chain(s[f..t].chars().rev())
.chain(s[t..].chars())
.collect();
Upvotes: 5
Reputation: 22203
Assuming a single occurence of the substring (or when you want to reverse all of them):
fn main() {
let s = String::from("the quick (brown (fox) jumps) over");
let to_rev = &"fox";
let ss = s.replace(to_rev, &to_rev.chars().rev().collect::<String>());
println!("{}", ss); // the quick (brown (xof) jumps) over
}
Upvotes: 1