35

I have a string with the value "Program", and I want to find the index of character 'g' in that string.

1
  • There are different kinds of indices into a String, because there are a number of levels at which you can count. Counting bytes gives the byte index (byte where a character starts), counting graphemes gives a grapheme index, counting code points gives a code point index. Commented Mar 17, 2022 at 13:22

4 Answers 4

68

Although a little more convoluted than I would like, another solution is to use the Chars iterator and its position() function:

"Program".chars().position(|c| c == 'g').unwrap()

find used in the accepted solution returns the byte offset and not necessarily the index of the character. It works fine with basic ASCII strings, such as the one in the question, and while it will return a value when used with multi-byte Unicode strings, treating the resulting value as a character index will cause problems.

This works:

let my_string = "Program";
let g_index = my_string.find("g");   // 3
let g: String = my_string.chars().skip(g_index).take(1).collect();
assert_eq!("g", g);   // g is "g"

This does not work:

let my_string = "プログラマーズ";
let g_index = my_string.find("グ");    // 6
let g: String = my_string.chars().skip(g_index).take(1).collect();
assert_eq!("グ", g);    // g is "ズ"
Sign up to request clarification or add additional context in comments.

1 Comment

Find returns the starting byte index, so it should not be used as a character index. So instead of using it with chars().skip(), it should be used to slice: let g: String = my_string[g_index..].chars().take(1).collect(); and that works fine no matter what kind of characters you use.
42

You are looking for the find method for String. To find the index of 'g' in "Program" you can do

"Program".find('g')

Docs on find.

1 Comment

Be careful, it's not the correct answer since "πx".find("x") returns byte index 2 not char index 1!
5

If your word has several g's you could use enumerate to find all indices:

"ඞggregate"
    .chars()
    .enumerate()
    .filter(|(_, c)| *c == 'g')
    .map(|(i, _)| i)
    .collect::<Vec<_>>();  // [1, 2, 5]

If the string contains ASCII characters only:

"aggregate"
    .bytes()
    .enumerate()
    .filter(|(_, b)| *b == b'g')
    .map(|(i, _)| i)
    .collect::<Vec<_>>();  // [1, 2, 5]

Playground

Comments

-1

Finding a character that satisfies some condition and then using its byte index works like this:

fn example(s: &str) -> Option<char> {
    // find (the byte index of) some character satisfying some condition
    if let Some(byte_index) = s
        .char_indices()
        .find_map(|(p, c)| c.is_numeric().then_some(p))
    {
        // split the string there for further processing
        let (_first, last): (&str, &str) = s.split_at(byte_index);
        // the character that we found
        let found_char: char = last.chars().next().unwrap();
        Some(found_char)
    } else {
        // no such character
        None
    }
}

1 Comment

str::char_indices returns character index, not byte index. Character can be encoded with multiple bytes in both utf-8 and utf-16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.