Опубликован релиз языка системного программирования Rust 1.50, основанного проектом Mozilla, но ныне развиваемого под покровительством независимой некоммерческой организации Rust Foundation. Язык сфокусирован на безопасной работе с памятью, обеспечивает автоматическое управление памятью и предоставляет средства для достижения высокого параллелизма выполнения заданий, при этом обходясь без использования сборщика мусора и runtime (runtime сводится к базовой инициализации и сопровождению стандартной библиотеки).
Автоматическое управление памятью в Rust избавляет разработчика от ошибок при манипулировании указателями и защищает от проблем, возникающих из-за низкоуровневой работы с памятью, таких как обращение к области памяти после её освобождения, разыменование нулевых указателей, выход за границы буфера и т.п. Для распространения библиотек, обеспечения сборки и управления зависимостями проектом развивается пакетный менеджер Cargo. Для размещения библиотек поддерживается репозиторий crates.io.
Основные новшества:
- Продолжена работа по стабилизации константных дженериков (“const generics“), которые теперь могут применяться для индексации массивов не только через оператор “[]”, но и при помощи типажей ops::Index и IndexMut (добавлены реализации ops::Index и IndexMut для массивов “[T; N]” с любым размером const N). fn second(container: &C) -> &C::Output where C: std::ops::Index + ?Sized, { &container[1] } fn main() { let array: [i32; 3] = [1, 2, 3]; assert_eq!(second(&array[..]), &2); // slices worked before assert_eq!(second(&array), &2); // now it also works directly }
- Стабилизировано применение любых const-значений при определении массивов с использованием синтаксиса “[x; N]”. Ранее при числе повторов “N” больше единицы const-выражения в значении “x” разрешалось использовать только для реализация типажа Copy. Теперь это ограничение снято. fn main() { const NONE: Option> = None; const EMPTY: Option> = Some(Vec::new()); let nones = [NONE; 10]; let empties = [EMPTY; 10]; }
- Разрешено безопасное назначение полей ManuallyDrop в объединениях (union). Ранее, в режиме safe могли использоваться только типажи Copy, так как имеется неопределённость по поводу того, какой вариант остаётся действительным после применения Drop. ManuallyDrop не требует Drop, поэтому назначение данных полей можно считать безопасным.
- Для типа File на Unix-системах реализована поддержка нишевого значения (niche) “-1”, которое кодирует ошибку при работе с файловым дескриптором. Под нишевым значением подразумевается особое значение, которое может влиять на оптимизацию размещения типа в памяти, но является недопустимым для типа (например, нишевым является значение 0 для типов NonZero). Структура File в окружениях Unix определяет файловый дескриптор, который не может быть отрицательным, но, при этом системные вызовы могут возвращать в дескрипторе значения “-1”, сигнализирующие об ошибке операции. Добавление нишевого значения позволило сделать размер Option идентичным размеру File.
- В разряд стабильных переведена новая порция API, в том числе стабилизированы методы:
- bool::then
- btree_map::Entry::or_insert_with_key
- f32::clamp
- f64::clamp
- hash_map::Entry::or_insert_with_key
- Ord::clamp
- RefCell::take
- slice::fill
- UnsafeCell::get_mut
- IpAddr::is_ipv4
Дополнительно можно отметить результаты сравнения производительности переключения контекста и потребления памяти при использовании потоков Rust async и потоков, предоставляемых ядром Linux. Время переключение контекста между async-задачами составило 0.2µs, а переключение контекста между потоками ядра в Linux – 1.7µs, но разница исчезает если переключение контекста вызвано готовностью ввода/вывода и в обоих случаях приближается к 1.7µs. Преимущества async также пропадают в случае привязки потока к одному ядру CPU.
~300ns
Время создания новой задачи составляет для async ~300ns, а для Linux-потоков – ~17µs. На обычном ноутбуке с 4-ядерным CPU и 32 ГБ ОЗУ не возникло проблем с созданием 150 тысяч async-задач, но удалось запустить только 80 тысяч Linux-потоков. Минимальное потребление памяти на задачу составило нескольких сотен байт для async и 20КБ (9.5КБ в пространстве пользователя + 10КБ в ядре) для Linux-потоков.