Reputation: 20144
I'm trying to do a simple string inversion in Rust, but I can't seem to figure out what functions to use in order to do this.
Say I have a string: ____x_xx__x
inverting this string will become: xxxx_x__xx_
I tried:
//res is the string I want to invert
for mut c in res.as_slice().chars() {
c =
match c {
'x' => '_',
_ => 'x'
};
};
but this warns me that the value c
is never read, so I guess the c
I am using is not actually a reference to the character in the slice?
Upvotes: 2
Views: 635
Reputation: 841
Here is my take:
fn new_str(s: &String) -> String {
let mut s2 = String::new();
for i in range(0,s.len()) {
s2 = s2.append(if s.as_slice().char_at(i) == '_' { "x" } else { "_" });
}
s2
}
fn in_place(s: &mut String) {
for _i in range(0,s.len()) {
let c = s.shift_char();
match c {
Some(x) => s.grow(1,if x == '_' { 'x' } else { '_' }),
None => return
}
}
}
fn main() {
let s = String::from_str("___xxx__x_");
let s2 = new_str(&s);
println!("{}",s);
println!("{}",s2);
let mut s3 = String::from_str("___xxx__x_");
in_place(&mut s3);
println!("{}",s3);
}
Output:
___xxx__x_
xxx___xx_x
xxx___xx_x
Annoyingly enough the in place version is O(n^2) since "shift_char" is O(n). I'm not sure how to alter a single character directly though.
Upvotes: 1
Reputation: 12016
fn main() {
let mut res = String::from_str("____x_xx__x").into_ascii();
for c in res.mut_iter() {
*c = match c.to_char() {
'x' => '_',
_ => 'x'
}.to_ascii();
};
println!("{}", res.into_string());
}
Upvotes: 3
Reputation: 90912
.chars()
yields char
instances which are not references in any way.
To be able to modify such a thing in place, you would need to have a mutable slice.
A String
is UTF-8 encoded; if you wish to do it in place, any character replacement must be for a character with the same number of bytes; the simplest case for that is ASCII. (And with your examples of x
and _
, it must be ASCII.)
The way to implement this in place would be as_mut_bytes()
and its mut_iter()
:
let bytes = unsafe { res.as_mut_slice() };
for c in bytes.mut_iter() {
*c = match *c {
'x' => '_',
_ if c.len_utf8_bytes() > 1 => fail!("you tried to make me make bad UTF-8"),
_ => 'x',
}
}
(I inserted the len_utf8_bytes()
branch to guarantee the UTF-8 invariant. It may be omitted—though putting it as a debug_assert!
is still probably a good idea—if you know you’re only dealing with ASCII.)
Alternatively, you could create a new string out of it. This would cope with non-ASCII strings.
res.chars().map(|c| if c == 'x' { '_' } else { 'x' }).collect::<String>()
Using codepoints as the boundaries isn’t very precise either, so let’s work with grapheme clusters for this case instead.
res.graphemes(true).map(|c| if c == "x" { '_' } else { 'x' }).collect::<String>()
Upvotes: 3