Выпуск языка программирования Rust 1.50

Опубликован релиз языка системного программирования 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
  • Признак “const”, определяющий возможность использования в любом контексте вместо констант, применён в методах
      IpAddr::is_ipv4
  • IpAddr::is_ipv6
  • Layout::size
  • Layout::align
  • Layout::from_size_align
  • pow для всех целых типов.
  • checked_pow для всех целых типов.
  • saturating_pow для всех целых типов.
  • wrapping_pow для всех целых типов.
  • next_power_of_two для всех беззнаковых целых типов.
  • checked_power_of_two для всех беззнаковых целых типов.
  • В пакетный менеджер cargo в команде “update” реализована опция “–workspace”, позволяющая ограничить обновление только членами выбранного рабочего пространства, не трогая их зависимости. В метаданные добавлено новое поле “doc” для определения того, что цель документирована. Запрещено применение git-зависимостей, в которых указано более одной ветки, тега или ревизии. Для загруженных в реестр crate-файлов обеспечена сборка с повторяемыми (reproducible) настройками, которые остаются неизменными для разных типов систем.
  • Обеспечена поддержка сборки целевой платформы x86_64-unknown-freebsd с полным инструментарием. Реализован третий уровень поддержки для платформ armv5te-unknown-linux-uclibceabi и aarch64-apple-ios-macabi. Третий уровень подразумевает базовую поддержку, но без автоматизированного тестирования, публикации официальных сборок и проверки возможности сборки кода.

    Дополнительно можно отметить результаты сравнения производительности переключения контекста и потребления памяти при использовании потоков 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-потоков.

  • Release. Ссылка here.