Примитивный тип 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_UTF841.93.0Максимальное количество байтов, необходимое для кодирования char в UTF-8
MAX_LEN_UTF1621.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) -> DecodeUtf161.52.0Создает итератор по UTF-16 кодамБезопасный
from_u32(i: u32) -> Option<char>1.52.0 (const: 1.67.0)Конвертирует u32 в charБезопасный
from_u32_unchecked(i: u32) -> char1.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) -> bool1.0.0 (const: 1.87.0)Проверяет, является ли символ цифрой в указанной системе счисленияБезопасный
to_digit(self, radix: u32) -> Option<u32>1.0.0 (const: 1.67.0)Конвертирует символ в цифру по основаниюБезопасный

Методы экранирования

МетодВерсияОписание
escape_unicode(self) -> EscapeUnicode1.0.0Возвращает итератор с шестнадцатеричным Unicode-экранированием
escape_debug(self) -> EscapeDebug1.20.0Возвращает итератор с отладочным экранированием
escape_default(self) -> EscapeDefault1.0.0Возвращает итератор с экранированием по умолчанию

Методы кодирования и длины

МетодВерсияОписание
len_utf8(self) -> usize1.0.0 (const: 1.52.0)Возвращает количество байтов для кодирования в UTF-8
len_utf16(self) -> usize1.0.0 (const: 1.52.0)Возвращает количество 16-битных единиц для кодирования в UTF-16
encode_utf8(self, dst: &mut [u8]) -> &mut str1.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) -> bool1.0.0Проверяет, является ли символ буквой (Alphabetic)
is_lowercase(self) -> bool1.0.0 (const: 1.84.0)Проверяет, является ли символ строчной буквой
is_uppercase(self) -> bool1.0.0 (const: 1.84.0)Проверяет, является ли символ прописной буквой
is_whitespace(self) -> bool1.0.0 (const: 1.87.0)Проверяет, является ли символ пробельным
is_alphanumeric(self) -> bool1.0.0Проверяет, является ли символ буквенно-цифровым
is_control(self) -> bool1.0.0Проверяет, является ли символ управляющим
is_numeric(self) -> bool1.0.0Проверяет, является ли символ числовым

Методы преобразования регистра

МетодВерсияОписание
to_lowercase(self) -> ToLowercase1.0.0Возвращает итератор строчного отображения
to_uppercase(self) -> ToUppercase1.0.0Возвращает итератор прописного отображения

Методы проверки ASCII

МетодВерсияОписание
is_ascii(&self) -> bool1.23.0 (const: 1.32.0)Проверяет, находится ли значение в диапазоне ASCII
as_ascii(&self) -> Option<AsciiChar>nightlyВозвращает AsciiChar, если значение в диапазоне ASCII
as_ascii_unchecked(&self) -> AsciiCharnightlyКонвертирует в ASCII без проверки (unsafe)
to_ascii_uppercase(&self) -> char1.23.0 (const: 1.52.0)Конвертирует в ASCII прописную букву
to_ascii_lowercase(&self) -> char1.23.0 (const: 1.52.0)Конвертирует в ASCII строчную букву
eq_ignore_ascii_case(&self, other: &char) -> bool1.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) -> bool1.24.0 (const: 1.47.0)Проверяет ASCII букву (A-Z, a-z)
is_ascii_uppercase(&self) -> bool1.24.0 (const: 1.47.0)Проверяет ASCII прописную букву (A-Z)
is_ascii_lowercase(&self) -> bool1.24.0 (const: 1.47.0)Проверяет ASCII строчную букву (a-z)
is_ascii_alphanumeric(&self) -> bool1.24.0 (const: 1.47.0)Проверяет ASCII буквенно-цифровой символ
is_ascii_digit(&self) -> bool1.24.0 (const: 1.47.0)Проверяет ASCII десятичную цифру (0-9)
is_ascii_octdigit(&self) -> boolnightlyПроверяет ASCII восьмеричную цифру (0-7)
is_ascii_hexdigit(&self) -> bool1.24.0 (const: 1.47.0)Проверяет ASCII шестнадцатеричную цифру
is_ascii_punctuation(&self) -> bool1.24.0 (const: 1.47.0)Проверяет ASCII пунктуацию
is_ascii_graphic(&self) -> bool1.24.0 (const: 1.47.0)Проверяет ASCII графический символ
is_ascii_whitespace(&self) -> bool1.24.0 (const: 1.47.0)Проверяет ASCII пробельный символ
is_ascii_control(&self) -> bool1.24.0 (const: 1.47.0)Проверяет ASCII управляющий символ

Реализации трейтов

Основные трейты

ТрейтОписание
CloneКлонирование символа
CopyКопирование символа
DebugОтладочное форматирование
DisplayПользовательское форматирование
DefaultЗначение по умолчанию ('\x00')
Eq, PartialEqСравнение на равенство
Ord, PartialOrdУпорядочивание
HashХеширование
FromStrПарсинг из строки

Конвертации

КонвертацияВерсияОписание
From<u8> for char1.13.0Преобразует байт (0x00..=0xFF) в символ
From<char> for u321.13.0Преобразует символ в u32
From<char> for u641.51.0Преобразует символ в u64
From<char> for u1281.51.0Преобразует символ в u128
From<char> for String1.46.0Создает String из символа
TryFrom<char> for u81.59.0Пытается преобразовать символ в u8
TryFrom<char> for u161.74.0Пытается преобразовать символ в u16
TryFrom<u32> for char1.34.0Пытается преобразовать u32 в символ

Сборщики

ТрейтВерсияОписание
FromIterator<char> for String1.0.0Создает String из итератора символов
FromIterator<&char> for String1.17.0Создает String из итератора ссылок на символы
Extend<char> for String1.0.0Расширяет String символами
Extend<&char> for String1.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());
}
}

Важные замечания

  1. char ≠ байт: char всегда занимает 4 байта, тогда как символы в UTF-8 строке могут занимать от 1 до 4 байт
  2. Суррогатные пары: char не может содержать суррогатные кодовые точки (0xD800-0xDFFF)
  3. Графические кластеры: Один видимый "символ" может состоять из нескольких char (например, 'é' = 'e' + комбинирующий акцент)
  4. Регистр не всегда обратим: to_lowercase() и to_uppercase() не всегда являются обратными операциями
  5. ASCII подмножество Unicode: Все ASCII символы являются валидными char, но не наоборот

Производительность

  • char всегда выровнен как u32, что обеспечивает быстрый доступ
  • Методы с пометкой const могут вычисляться во время компиляции
  • Кодирование/декодирование UTF-8/UTF-16 оптимизировано
  • Проверки категорий Unicode используют предварительно вычисленные таблицы