str::encode_utf16¶
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.
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¶
String::from_utf16— the trip backString::from_utf16le— when the input is bytes with a known endiannessstr::chars— the count that surrogate pairs make differstr::len— the UTF-8 byte count, different again
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