Skip to content

str::encode_utf16

str methods · Strings

Level: reference · for working programmers

One line: An iterator of u16 code units in UTF-16 — the encoding Windows APIs, Java and JavaScript use internally.

pub fn encode_utf16(&self) -> EncodeUtf16<'_>

Stable since 1.8.0.

Rust strings are UTF-8; a great deal of the outside world is UTF-16. This is the bridge out, and String::from_utf16 is the bridge back.

A character outside the Basic Multilingual Plane — emoji, most historic scripts — encodes as a surrogate pair: two u16 values for one char. So the item count is neither the byte count nor the character count, and "length" in a Windows or JavaScript API means something different again from either of Rust's two answers.

The iterator borrows, so nothing is allocated until you collect. Windows APIs want a NUL-terminated Vec<u16>, which is s.encode_utf16().chain(std::iter::once(0)).collect().

Example

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

fn main() {
    let s = "hi";
    println!("{:?}", s.encode_utf16().collect::<Vec<u16>>());

    // Outside the BMP: one char, two code units.
    let emoji = "👋";
    let units: Vec<u16> = emoji.encode_utf16().collect();
    println!("{units:?}");
    println!("{} chars, {} utf16 units, {} utf8 bytes",
             emoji.chars().count(), units.len(), emoji.len());

    // Three different "lengths" for the same text.
    let mixed = "a👋b";
    println!("utf8 {} / chars {} / utf16 {}",
             mixed.len(), mixed.chars().count(), mixed.encode_utf16().count());

    // The round trip.
    let wide: Vec<u16> = mixed.encode_utf16().collect();
    println!("{:?}", String::from_utf16(&wide));

    // A NUL-terminated buffer, as a Windows API wants.
    let wide_z: Vec<u16> = "ok".encode_utf16().chain(std::iter::once(0)).collect();
    println!("{wide_z:?}");
}

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

[104, 105]
[55357, 56395]
1 chars, 2 utf16 units, 4 utf8 bytes
utf8 6 / chars 3 / utf16 4
Ok("a👋b")
[111, 107, 0]

See also

str::encode_utf16 in the standard library ↗

Po polsku

Rust mówi w UTF-8, ale Windows, Java i JavaScript trzymają tekst w UTF-16 — encode_utf16 jest mostem na zewnątrz, a String::from_utf16 mostem z powrotem. Dla polskiego tekstu ten most jest wyjątkowo spokojny: wszystkie nasze litery leżą w BMP, więc każda zajmuje dokładnie jedną jednostkę u16 i liczba jednostek pokrywa się z liczbą znaków — i właśnie dlatego pułapka bez trudu przechodzi przez testy. Wystarczy jedno emoji, żeby pojawiła się para zastępcza (surrogate pair): jeden char zapisany dwiema jednostkami, a wtedy ten sam tekst ma trzy różne „długości” — bajty UTF-8, znaki i jednostki UTF-16 — i to ta trzecia jest tą, którą raportuje API Windowsa. Iterator tylko pożycza, więc nic nie jest alokowane aż do collect; funkcje …W z WinAPI chcą bufora zakończonego zerem, czyli s.encode_utf16().chain(std::iter::once(0)).collect::<Vec<u16>>().

Szukaj po polsku: para zastępcza · surogaty w UTF-16 · UTF-16 a UTF-8 · rust encode_utf16 windows · rust surrogate pair