Char : how to get next char in rust


#1

if i want to get the next character from alphabets how to do it. let’s suppose i have string “hello” and i want to move each character by 4 position “lipps”. how to do it


#2

Here’s a sketch of a possible approach:

fn add1_char(c: char) -> char {
    std::char::from_u32(c as u32 + 1).unwrap_or(c)
}

fn add1_str(s: &str) -> String {
    s.chars().map(add1_char).collect()
}

#3

If I understand @Harry’s intent correctly, he will probably also want to add some range checks (is this an alphabetic character ?) and some modulo-based wraparound (to handle the end of the alphabet) to this sketch.


#4

Just to have mentioned it: depending on what it is that you want you might be better off using the unicode_segmentation crate, which provides an iterator that yields graphemes rather than chars.
This distinction doesn’t matter when you just want bytes or chars, but is very important when working with non-ascii (i.e. Unicode) texts (e.g. in UTF-8).


#5

Here is simple example satisfying your requirements:

fn move_shift(data: &String, shift: usize) -> String {
    data.chars().map(|c| (c as u8 + shift as u8) as char).collect::<String>()
}

#6

yes.


#7

If your alphabet is small Latin letters from a to z, then this code will work:

#[derive(Debug)]
struct MyError;

fn shift(input: &str, shift: i8) -> Result<String, MyError> {
    let mut buf = Vec::with_capacity(input.len());
    for byte in input.bytes() {
        match byte {
            b'a'..=b'z' => {
                let mut val = byte as i16 + shift as i16;
                if val > b'z' as i16 || val < b'a' as i16 {
                    val -= b'a' as i16;
                    val = if val >= 0 {
                        (val % (b'z' as i16)) + b'a' as i16
                    } else {
                        1 + b'z' as i16 + (val % (b'z' as i16))
                    }
                }
                buf.push(val as u8);
            }
            _ => Err(MyError)?,
        }
    }
    let out = String::from_utf8(buf)
        .expect("we are sure that all bytes fall into ASCII range");
    Ok(out)
}

fn main() {
    println!("{:?}", shift("hello", 4).unwrap());
    println!("{:?}", shift("abcdef", -3).unwrap());
}

It prints “lipps” and “xyzabc”. It can be done without allocating additionall string, though code will be a bit more complex.