Skip to content

str::as_bytes

str methods · Strings

Level: reference · for working programmers

One line: Borrows the string's UTF-8 bytes as &[u8] — free, since that is already how the text is stored.

pub const fn as_bytes(&self) -> &[u8]

Stable since 1.0.0. Usable in a const context.

No conversion happens. A &str is a &[u8] plus a promise that the bytes are valid UTF-8, so as_bytes hands back the slice and drops the promise. Going the other way costs a check — see str::from_utf8.

Reach for it when the job is genuinely about bytes: writing to a socket, hashing, comparing against a byte literal, or an ASCII scan where indexing is legal because every byte is one character.

fn main() {
    let s = "hello";
    println!("{}", s.as_bytes()[0]);       // 104 — you cannot write s[0]
    println!("{:?}", &b"hello"[..]);       // b"..." is the byte-string literal
}

The trap is treating the result as characters. s.as_bytes()[i] is a byte, and on any non-ASCII text a single character occupies several of them, so a loop over bytes that assumes "one byte, one character" is right on English input and wrong on the first accented name. s.as_bytes().len() is exactly len.

The bytes are borrowed, so the string has to outlive them. For an owned Vec<u8> use String::into_bytes, which is also free — it is the same allocation, relabelled.

Example

str_as_bytes.rs in full — pasted here by tools/run_examples.py from the file CI compiles and runs.

fn main() {
    let s = "hé!";
    println!("{:?}", s.as_bytes());          // [104, 195, 169, 33]
    println!("{} bytes, {} chars", s.as_bytes().len(), s.chars().count());

    // A byte-literal comparison, which is what as_bytes is really for.
    println!("{}", "GET /".as_bytes().starts_with(b"GET"));

    // An ASCII-only scan: legal because every byte here is one character,
    // so a byte index and a character index are the same number.
    let code = "A7";
    let bytes = code.as_bytes();
    println!("letter {:?}, digit {}", bytes[0] as char, bytes[1] - b'0');

    // The trap: byte 1 of "hé!" is half of 'é'.
    println!("byte 1 = {}, char 1 = {:?}", s.as_bytes()[1], s.chars().nth(1).unwrap());
}

Verified output of str_as_bytes.rs — regenerated by tools/run_examples.py, never hand-typed.

[104, 195, 169, 33]
4 bytes, 3 chars
true
letter 'A', digit 7
byte 1 = 195, char 1 = 'é'

See also

str::as_bytes in the standard library ↗

Po polsku

as_bytes niczego nie konwertuje i nic nie kosztuje: &str jest &[u8] plus obietnica, że te bajty to poprawny UTF-8, a ta metoda oddaje sam wycinek i obietnicę porzuca — dlatego działa w kontekście const. Droga powrotna kosztuje już sprawdzenie, stąd osobne from_utf8. Pułapka jest ta, która przy polskim tekście wraca zawsze: wynik to bajty, a nie znaki. Widać to na przykładzie ze strony — "hé!" ma cztery bajty i trzy znaki, a as_bytes()[1] to 195, czyli pierwsza połowa é; z "żółw" wyjdzie siedem bajtów na cztery litery i żaden pojedynczy element nie jest literą.

Sięgaj po as_bytes, kiedy zadanie naprawdę dotyczy bajtów — zapis do gniazda, haszowanie, porównanie z literałem b"GET", skan po czystym ASCII — a nie kiedy chcesz „wziąć i-ty znak”; do tego są chars() i char_indices(). Bajty są pożyczone, więc łańcuch musi je przeżyć; gdy potrzebny jest własny Vec<u8>, użyj String::into_bytes, które też jest darmowe, bo to ta sama alokacja, tylko inaczej opisana.

Szukaj po polsku: bajty a znaki w UTF-8 · wycinek bajtów · rust str as_bytes · rust str is utf-8 bytes · rust from_utf8