Примитивный тип char
Версия 1.0.0
Символьный тип.
Тип char представляет отдельный символ. Более конкретно, поскольку понятие "символ" не является четко определенным в Unicode, char представляет собой "скалярное значение Unicode" (Unicode scalar value).
Эта документация описывает ряд методов и реализаций трейтов для типа char. По техническим причинам существует дополнительная, отдельная документация в модуле std::char.
Валидность и размещение
char — это "скалярное значение Unicode", которое является любым "кодовой точкой Unicode", кроме суррогатных кодовых точек. Это имеет фиксированное числовое определение: кодовые точки находятся в диапазоне от 0 до 0x10FFFF включительно. Суррогатные кодовые точки, используемые UTF-16, находятся в диапазоне от 0xD800 до 0xDFFF.
Нельзя создать ни один char, будь то литерал или во время выполнения, который не является скалярным значением Unicode. Нарушение этого правила вызывает неопределенное поведение.
#![allow(unused)] fn main() { // Каждая из этих строк вызывает ошибку компиляции ['\u{D800}', '\u{DFFF}', '\u{110000}']; }
#![allow(unused)] fn main() { // Паника; from_u32 возвращает None char::from_u32(0xDE01).unwrap(); // Неопределенное поведение let _ = unsafe { char::from_u32_unchecked(0x110000) }; }
Скалярные значения Unicode также являются точным набором значений, которые могут быть закодированы в UTF-8. Поскольку значения char являются скалярными значениями Unicode и функции могут предполагать, что входящие значения str являются валидным UTF-8, безопасно хранить любой char в str или читать любой символ из str как char.
Пробел в валидных значениях char понятен компилятору, поэтому в примере ниже два диапазона считаются покрывающими весь диапазон возможных значений char, и нет ошибки для неисчерпывающего сопоставления.
#![allow(unused)] fn main() { let c: char = 'a'; match c { '\0' ..= '\u{D7FF}' => false, '\u{E000}' ..= '\u{10FFFF}' => true, }; }
Все скалярные значения Unicode являются валидными значениями char, но не все они представляют реальный символ. Многие скалярные значения Unicode в настоящее время не назначены символу, но могут быть назначены в будущем ("зарезервированы"); некоторые никогда не будут символами ("несимволы"); а некоторые могут иметь разное значение для разных пользователей ("частное использование").
char гарантированно имеет тот же размер, выравнивание и ABI вызова функций, что и u32 на всех платформах.
#![allow(unused)] fn main() { use std::alloc::Layout; assert_eq!(Layout::new::<char>(), Layout::new::<u32>()); }
Представление
char всегда имеет размер четыре байта. Это представление отличается от представления символа как части String. Например:
#![allow(unused)] fn main() { let v = vec!['h', 'e', 'l', 'l', 'o']; // пять элементов по четыре байта каждый assert_eq!(20, v.len() * size_of::<char>()); let s = String::from("hello"); // пять элементов по одному байту на элемент assert_eq!(5, s.len() * size_of::<u8>()); }
Как всегда, помните, что человеческая интуиция относительно "символа" может не соответствовать определениям Unicode. Например, несмотря на внешнее сходство, символ 'é' является одной кодовой точкой Unicode, а 'é' — двумя кодовыми точками Unicode:
#![allow(unused)] fn main() { let mut chars = "é".chars(); // U+00e9: 'latin small letter e with acute' assert_eq!(Some('\u{00e9}'), chars.next()); assert_eq!(None, chars.next()); let mut chars = "é".chars(); // U+0065: 'latin small letter e' assert_eq!(Some('\u{0065}'), chars.next()); // U+0301: 'combining acute accent' assert_eq!(Some('\u{0301}'), chars.next()); assert_eq!(None, chars.next()); }
Это означает, что содержимое первой строки выше поместится в char, а содержимое второй строки — нет. Попытка создать символьный литерал с содержимым второй строки вызывает ошибку:
#![allow(unused)] fn main() { error: character literal may only contain one codepoint: 'é' let c = 'é'; ^^^ }
Еще одно следствие фиксированного 4-байтового размера char заключается в том, что обработка каждого символа может использовать значительно больше памяти:
#![allow(unused)] fn main() { let s = String::from("love: ❤️"); let v: Vec<char> = s.chars().collect(); assert_eq!(12, size_of_val(&s[..])); assert_eq!(32, size_of_val(&v[..])); }
Реализации
Константы
| Константа | Значение | Версия | Описание |
|---|---|---|---|
MIN | '\0' | 1.83.0 | Наименьшая допустимая кодовая точка, которую может иметь char |
MAX | '\u{10ffff}' | 1.52.0 | Наибольшая допустимая кодовая точка, которую может иметь char |
MAX_LEN_UTF8 | 4 | 1.93.0 | Максимальное количество байтов, необходимое для кодирования char в UTF-8 |
MAX_LEN_UTF16 | 2 | 1.93.0 | Максимальное количество 2-байтовых единиц для кодирования char в UTF-16 |
REPLACEMENT_CHARACTER | '�' | 1.52.0 | Символ замены U+FFFD, используется для представления ошибок декодирования |
UNICODE_VERSION | (u8, u8, u8) | 1.52.0 | Версия Unicode, на которой основаны методы char и str |
Методы для создания и преобразования
| Метод | Версия | Описание | Безопасность |
|---|---|---|---|
decode_utf16<I>(iter: I) -> DecodeUtf16 | 1.52.0 | Создает итератор по UTF-16 кодам | Безопасный |
from_u32(i: u32) -> Option<char> | 1.52.0 (const: 1.67.0) | Конвертирует u32 в char | Безопасный |
from_u32_unchecked(i: u32) -> char | 1.52.0 (const: 1.81.0) | Конвертирует u32 в char без проверки | unsafe |
from_digit(num: u32, radix: u32) -> Option<char> | 1.52.0 (const: 1.67.0) | Конвертирует цифру в символ по основанию | Безопасный |
is_digit(self, radix: u32) -> bool | 1.0.0 (const: 1.87.0) | Проверяет, является ли символ цифрой в указанной системе счисления | Безопасный |
to_digit(self, radix: u32) -> Option<u32> | 1.0.0 (const: 1.67.0) | Конвертирует символ в цифру по основанию | Безопасный |
Методы экранирования
| Метод | Версия | Описание |
|---|---|---|
escape_unicode(self) -> EscapeUnicode | 1.0.0 | Возвращает итератор с шестнадцатеричным Unicode-экранированием |
escape_debug(self) -> EscapeDebug | 1.20.0 | Возвращает итератор с отладочным экранированием |
escape_default(self) -> EscapeDefault | 1.0.0 | Возвращает итератор с экранированием по умолчанию |
Методы кодирования и длины
| Метод | Версия | Описание |
|---|---|---|
len_utf8(self) -> usize | 1.0.0 (const: 1.52.0) | Возвращает количество байтов для кодирования в UTF-8 |
len_utf16(self) -> usize | 1.0.0 (const: 1.52.0) | Возвращает количество 16-битных единиц для кодирования в UTF-16 |
encode_utf8(self, dst: &mut [u8]) -> &mut str | 1.15.0 (const: 1.83.0) | Кодирует символ в UTF-8 в буфер |
encode_utf16(self, dst: &mut [u16]) -> &mut [u16] | 1.15.0 (const: 1.84.0) | Кодирует символ в UTF-16 в буфер |
Методы категорий Unicode
| Метод | Версия | Описание |
|---|---|---|
is_alphabetic(self) -> bool | 1.0.0 | Проверяет, является ли символ буквой (Alphabetic) |
is_lowercase(self) -> bool | 1.0.0 (const: 1.84.0) | Проверяет, является ли символ строчной буквой |
is_uppercase(self) -> bool | 1.0.0 (const: 1.84.0) | Проверяет, является ли символ прописной буквой |
is_whitespace(self) -> bool | 1.0.0 (const: 1.87.0) | Проверяет, является ли символ пробельным |
is_alphanumeric(self) -> bool | 1.0.0 | Проверяет, является ли символ буквенно-цифровым |
is_control(self) -> bool | 1.0.0 | Проверяет, является ли символ управляющим |
is_numeric(self) -> bool | 1.0.0 | Проверяет, является ли символ числовым |
Методы преобразования регистра
| Метод | Версия | Описание |
|---|---|---|
to_lowercase(self) -> ToLowercase | 1.0.0 | Возвращает итератор строчного отображения |
to_uppercase(self) -> ToUppercase | 1.0.0 | Возвращает итератор прописного отображения |
Методы проверки ASCII
| Метод | Версия | Описание |
|---|---|---|
is_ascii(&self) -> bool | 1.23.0 (const: 1.32.0) | Проверяет, находится ли значение в диапазоне ASCII |
as_ascii(&self) -> Option<AsciiChar> | nightly | Возвращает AsciiChar, если значение в диапазоне ASCII |
as_ascii_unchecked(&self) -> AsciiChar | nightly | Конвертирует в ASCII без проверки (unsafe) |
to_ascii_uppercase(&self) -> char | 1.23.0 (const: 1.52.0) | Конвертирует в ASCII прописную букву |
to_ascii_lowercase(&self) -> char | 1.23.0 (const: 1.52.0) | Конвертирует в ASCII строчную букву |
eq_ignore_ascii_case(&self, other: &char) -> bool | 1.23.0 (const: 1.52.0) | Проверяет равенство без учета регистра ASCII |
make_ascii_uppercase(&mut self) | 1.23.0 (const: 1.84.0) | Преобразует в ASCII прописную на месте |
make_ascii_lowercase(&mut self) | 1.23.0 (const: 1.84.0) | Преобразует в ASCII строчную на месте |
Детальные методы проверки ASCII
| Метод | Версия | Описание |
|---|---|---|
is_ascii_alphabetic(&self) -> bool | 1.24.0 (const: 1.47.0) | Проверяет ASCII букву (A-Z, a-z) |
is_ascii_uppercase(&self) -> bool | 1.24.0 (const: 1.47.0) | Проверяет ASCII прописную букву (A-Z) |
is_ascii_lowercase(&self) -> bool | 1.24.0 (const: 1.47.0) | Проверяет ASCII строчную букву (a-z) |
is_ascii_alphanumeric(&self) -> bool | 1.24.0 (const: 1.47.0) | Проверяет ASCII буквенно-цифровой символ |
is_ascii_digit(&self) -> bool | 1.24.0 (const: 1.47.0) | Проверяет ASCII десятичную цифру (0-9) |
is_ascii_octdigit(&self) -> bool | nightly | Проверяет ASCII восьмеричную цифру (0-7) |
is_ascii_hexdigit(&self) -> bool | 1.24.0 (const: 1.47.0) | Проверяет ASCII шестнадцатеричную цифру |
is_ascii_punctuation(&self) -> bool | 1.24.0 (const: 1.47.0) | Проверяет ASCII пунктуацию |
is_ascii_graphic(&self) -> bool | 1.24.0 (const: 1.47.0) | Проверяет ASCII графический символ |
is_ascii_whitespace(&self) -> bool | 1.24.0 (const: 1.47.0) | Проверяет ASCII пробельный символ |
is_ascii_control(&self) -> bool | 1.24.0 (const: 1.47.0) | Проверяет ASCII управляющий символ |
Реализации трейтов
Основные трейты
| Трейт | Описание |
|---|---|
Clone | Клонирование символа |
Copy | Копирование символа |
Debug | Отладочное форматирование |
Display | Пользовательское форматирование |
Default | Значение по умолчанию ('\x00') |
Eq, PartialEq | Сравнение на равенство |
Ord, PartialOrd | Упорядочивание |
Hash | Хеширование |
FromStr | Парсинг из строки |
Конвертации
| Конвертация | Версия | Описание |
|---|---|---|
From<u8> for char | 1.13.0 | Преобразует байт (0x00..=0xFF) в символ |
From<char> for u32 | 1.13.0 | Преобразует символ в u32 |
From<char> for u64 | 1.51.0 | Преобразует символ в u64 |
From<char> for u128 | 1.51.0 | Преобразует символ в u128 |
From<char> for String | 1.46.0 | Создает String из символа |
TryFrom<char> for u8 | 1.59.0 | Пытается преобразовать символ в u8 |
TryFrom<char> for u16 | 1.74.0 | Пытается преобразовать символ в u16 |
TryFrom<u32> for char | 1.34.0 | Пытается преобразовать u32 в символ |
Сборщики
| Трейт | Версия | Описание |
|---|---|---|
FromIterator<char> for String | 1.0.0 | Создает String из итератора символов |
FromIterator<&char> for String | 1.17.0 | Создает String из итератора ссылок на символы |
Extend<char> for String | 1.0.0 | Расширяет String символами |
Extend<&char> for String | 1.2.0 | Расширяет String ссылками на символы |
Специальные трейты
| Трейт | Описание |
|---|---|
Pattern | Использование символа как шаблона для поиска в строках |
RangePattern | Использование символа в диапазонах шаблонов |
Step | Использование символа в итераторах по диапазонам |
Автоматические реализации трейтов
#![allow(unused)] fn main() { impl Freeze for char impl RefUnwindSafe for char impl Send for char impl Sync for char impl Unpin for char impl UnwindSafe for char }
Примеры использования
#![allow(unused)] fn main() { // Создание символов let heart = '❤'; let sigma = 'Σ'; let emoji = '🚀'; // Проверка категорий assert!('A'.is_alphabetic()); assert!('5'.is_numeric()); assert!(' '.is_whitespace()); assert!('a'.is_lowercase()); assert!('Z'.is_uppercase()); // Преобразование регистра assert_eq!('A'.to_lowercase().to_string(), "a"); assert_eq!('ß'.to_uppercase().to_string(), "SS"); assert_eq!('i'.to_uppercase().to_string(), "I"); // не зависит от локали // Работа с ASCII assert!('a'.is_ascii()); assert!('❤'.is_ascii()); // false assert_eq!('A'.to_ascii_lowercase(), 'a'); assert_eq!('z'.to_ascii_uppercase(), 'Z'); // Кодирование let mut buf = [0; 4]; let s = 'ß'.encode_utf8(&mut buf); assert_eq!(s, "ß"); assert_eq!(s.len(), 2); // Цифры assert_eq!('7'.to_digit(10), Some(7)); assert_eq!('f'.to_digit(16), Some(15)); assert_eq!(char::from_digit(15, 16), Some('f')); // Экранирование assert_eq!('"'.escape_default().to_string(), "\\\""); assert_eq!('\n'.escape_debug().to_string(), "\\n"); assert_eq!('❤'.escape_unicode().to_string(), "\\u{2764}"); // Диапазоны символов for c in 'a'..='z' { assert!(c.is_ascii_lowercase()); } }
Важные замечания
- char ≠ байт:
charвсегда занимает 4 байта, тогда как символы в UTF-8 строке могут занимать от 1 до 4 байт - Суррогатные пары:
charне может содержать суррогатные кодовые точки (0xD800-0xDFFF) - Графические кластеры: Один видимый "символ" может состоять из нескольких
char(например, 'é' = 'e' + комбинирующий акцент) - Регистр не всегда обратим:
to_lowercase()иto_uppercase()не всегда являются обратными операциями - ASCII подмножество Unicode: Все ASCII символы являются валидными
char, но не наоборот
Производительность
charвсегда выровнен какu32, что обеспечивает быстрый доступ- Методы с пометкой
constмогут вычисляться во время компиляции - Кодирование/декодирование UTF-8/UTF-16 оптимизировано
- Проверки категорий Unicode используют предварительно вычисленные таблицы