First character of a string in Rust: breaking down a popular question

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

First character of a string in Rust: breaking down a popular Stack Overflow question

\n\n

Have you ever wondered why a seemingly simple question, "How to get the first character from a string?" in Rust sparks so much debate and discussion? On Stack Overflow, this question has gathered thousands of views and dozens of answers. And this is no accident.

\n\n

Unlike many other languages where strings are just sequences of bytes, Rust approaches this issue with particular care. The reason lies in Unicode and UTF-8. In Rust, strings (the &str and String types) are always valid UTF-8 sequences. This means that one "character" can take from 1 to 4 bytes. Simply taking the first byte is incorrect — it might only be part of a character.

\n\n

In this article, we will break down all the main ways to get the first character of a string in Rust, compare their performance, ease of use, and discuss potential pitfalls. Let's go!

\n\n

Why can't you just use [0]?

\n\n

If you come from the world of C, Java, or Python, your first thought might be:

\n\n
let s = "Привет, мир!";\nlet first = s[0]; // Error!\n
\n\n

In Rust, this will not compile. Strings are indexed by bytes, not by characters. s[0] attempts to get the first byte. For Latin characters this would work (the character 'H' takes 1 byte), but for Cyrillic, 'П' takes 2 bytes. Rust does not allow you to accidentally get half a character — this is one of its key safety features.

\n\n

Instead, we need to use special methods that account for Unicode.

\n\n

Method 1: .chars().next() — the safest and most understandable

\n\n

This is the most recommended way to get the first character. The .chars() method returns an iterator over characters (type char), where each character is a complete Unicode unit. Then we take the first element of the iterator.

\n\n
fn main() {\n    let text = &quot;Привет&quot;;\n    \n    let first_char: Option<char> = text.chars().next();\n    \n    match first_char {\n        Some(c) => println!(&quot;First character: {}&quot;, c),\n        None => println!(&quot;String is empty&quot;),\n    }\n}\n
\n\n

Result: First character: П

\n\n

Advantages:

\n
    \n
  • Works with any Unicode characters, including emojis, hieroglyphs, and special characters.
  • \n
  • Returns Option<char>, which forces you to handle the case of an empty string.
  • \n
  • Very readable code.
  • \n
\n\n

Disadvantages:

\n
    \n
  • The .chars() iterator traverses the entire string (O(n) in the worst case), even if you only need the first character. However, in practice this is very fast, as the iterator is lazy.
  • \n
\n\n

Method 2: .bytes().next() — for working with bytes (caution!)

\n\n

Sometimes you really need the first byte, not the character. For example, when working with binary protocols or ASCII text. In this case, use .bytes().

\n\n
fn main() {\n    let text = &quot;Hello&quot;;\n    \n    let first_byte: Option<u8> = text.bytes().next();\n    \n    match first_byte {\n        Some(b) => println!(&quot;First byte: {} (character '{}')&quot;, b, b as char),\n        None => println!(&quot;String is empty&quot;),\n    }\n}\n
\n\n

Result: First byte: 72 (character 'H')

\n\n

Warning: If the string contains multi-byte characters (Cyrillic, emojis), the first byte will not be a complete character. For the string "Привет", .bytes().next() will return 208, which is the first byte of the two-byte character 'П'. This is not the same as the character itself.

\n\n

When to use: Only when you are sure you are working with an ASCII string, or

Blogs

Book Recommendations