Rust 1.98 — algebraic-операции с float и быстрое форматирование целых чисел

Rust 1.98.0 вышел 20 августа 2026 года и добавил в стабильную ветку новые algebraic_* методы для f32 и f64, позволяющие компилятору свободнее оптимизировать вычисления с плавающей точкой. Релиз также вводит NumBuffer и format_into для быстрого преобразования целых чисел в строки, расширяет стандартную библиотеку и усиливает ряд проверок совместимости.

Rust - версия 1.98
Rust - версия 1.98

Algebraic-операции дают компилятору больше свободы при работе с float

Главное изменение Rust 1.98 связано с новыми методами algebraic_add, algebraic_sub, algebraic_mul, algebraic_div и algebraic_rem для типов f32 и f64. Они позволяют разработчику явно указать, что конкретное вычисление допускает преобразования на основе обычных алгебраических правил, даже если такие преобразования могут менять точный результат вычислений в формате IEEE 754.

Обычные операции с плавающей точкой в Rust сохраняют строгую семантику порядка вычислений. Например, выражение:

let x = ((a + b) + c) + d;

не должно произвольно превращаться компилятором в:

let x = (a + c) + (b + d);

Для вещественных чисел такие выражения математически эквивалентны, но для конечной двоичной точности f32 и f64 результаты могут отличаться из-за округления. Именно поэтому обычная арифметика Rust ограничивает часть агрессивных оптимизаций.

С Rust 1.98 разработчик может явно разрешить подобные преобразования:

let x = a
    .algebraic_add(b)
    .algebraic_add(c)
    .algebraic_add(d);

После этого компилятор получает право перегруппировывать вычисления, сокращать критический путь, объединять операции и в ряде случаев лучше использовать SIMD-векторизацию.

По описанию команды Rust, набор разрешённых оптимизаций специально не фиксируется. Поведение может напоминать отдельные эффекты -ffast-math в других компиляторах, но речь не идёт о глобальном переключателе для всей программы: разработчик применяет новые методы только в тех местах, где готов принять ослабление численной воспроизводимости.

Официальная документация отдельно предупреждает, что при использовании algebraic-операций:

  • компилятор может менять порядок вычислений;
  • деление может преобразовываться в умножение на обратное значение;
  • поведение NaN, бесконечностей и -0.0 может отличаться от ожидаемого при строгой арифметике;
  • точная погрешность результата не гарантируется;
  • одинаковые исходные значения не обязаны всегда давать побитно идентичный результат;
  • операции при этом не создают undefined behavior.

Практически это делает новые методы интересными для численных циклов, обработки сигналов, графики, физических расчётов, машинного обучения и других задач, где скорость важнее строгой воспроизводимости каждого бита результата.

Для финансовых вычислений, криптографии, тестов с точным сравнением значений и алгоритмов, где последовательность округлений является частью логики, обычные +, -, *, / и % остаются более безопасным выбором.

Официальное объявление Rust 1.98.0

NumBuffer и format_into сокращают накладные расходы при преобразовании чисел

Второе заметное изменение Rust 1.98 — стабилизация core::fmt::NumBuffer и метода format_into для примитивных целочисленных типов.

NumBuffer<T> представляет собой небольшой буфер, размер которого достаточен для хранения десятичного представления любого значения соответствующего целочисленного типа. Метод format_into записывает число непосредственно в этот буфер и возвращает &str, заимствованный из него.

Пример для u32:

use core::fmt::NumBuffer;

let mut buf = NumBuffer::new();

let value = 1972u32;
let text = value.format_into(&mut buf);

assert_eq!(text, "1972");

Буфер можно использовать повторно:

use core::fmt::NumBuffer;

let mut buf = NumBuffer::new();

assert_eq!(10u64.format_into(&mut buf), "10");
assert_eq!(42u64.format_into(&mut buf), "42");
assert_eq!(u64::MAX.format_into(&mut buf), u64::MAX.to_string());

В отличие от универсальной инфраструктуры форматирования через write!, новый API позволяет избежать части динамической диспетчеризации и связанных с ней накладных расходов. Команда Rust указывает, что в тестах format_into показывает производительность, сопоставимую с популярным crate itoa.

Это не означает автоматического исчезновения сторонних библиотек форматирования. Они могут предлагать дополнительные возможности или уже быть глубоко встроены в существующие проекты. Но для кода, которому требуется только быстро преобразовывать целые числа в десятичный текст, стандартная библиотека теперь закрывает гораздо больше сценариев без внешней зависимости.

Такая оптимизация полезна прежде всего в:

  • логгерах;
  • сериализаторах;
  • HTTP- и сетевых библиотеках;
  • генераторах текстовых протоколов;
  • системах телеметрии;
  • коде с большим количеством числового форматирования.

Стандартная библиотека получила новые стабильные API

Rust 1.98 расширяет стабильную стандартную библиотеку сразу несколькими группами методов.

Среди наиболее заметных:

APIНазначение
str::substr_rangeПолучение диапазона байтов, соответствующего подстроке
[T]::subslice_rangeОпределение диапазона исходного среза для subslice
core::fmt::NumBufferБуфер для десятичного форматирования целых чисел
<integer>::format_intoФорматирование целого числа в NumBuffer
f32/f64::algebraic_*Арифметика с разрешёнными алгебраическими оптимизациями
NonZero<integer>::from_str_radixПарсинг NonZero из строки с указанным основанием
String::from_utf16le / from_utf16beСоздание строки из UTF-16 с заданным порядком байтов
[T]::strip_circumfixУдаление заданных элементов одновременно с начала и конца
str::strip_circumfixАналогичная операция для строк
Atomic<T>::from_mutРабота с атомарным представлением изменяемого значения
Atomic<T>::get_mut_sliceПолучение изменяемого среза из атомарного среза
Atomic<T>::from_mut_sliceПредставление изменяемого среза как среза атомарных значений

Появление String::from_utf16le, String::from_utf16be и их lossy-вариантов особенно удобно для кода, который работает с бинарными форматами, Windows-данными, сетевыми протоколами или файлами, где порядок байтов UTF-16 известен заранее.

Методы str::substr_range и [T]::subslice_range упрощают обратную задачу: определить, где именно заимствованный фрагмент расположен внутри исходной строки или среза. Ранее для подобных операций разработчики часто вычисляли смещения вручную через указатели или вспомогательные функции.

ManuallyDrop и Box получили стабильную гарантию поведения

Rust 1.98 также закрепляет в документации исправление взаимодействия ManuallyDrop и Box.

До Rust 1.96 следующий сценарий считался undefined behavior:

let mut x = ManuallyDrop::new(Box::new(1));
unsafe { ManuallyDrop::drop(&mut x) };
let x = x;

Причина заключалась в том, что после освобождения Box его последующее перемещение считалось недопустимым, а это правило распространялось и на ManuallyDrop<Box<_>>.

В Rust 1.96 поведение компилятора было исправлено. В Rust 1.98 команда проекта оформила это как стабильную гарантию в документации: перемещение ManuallyDrop<Box<_>> после ручного уничтожения содержащегося Box больше не рассматривается как undefined behavior только из-за самого факта такого перемещения.

Изменение особенно значимо для авторов низкоуровневых контейнеров, FFI-обвязок, unsafe-библиотек и собственного кода управления ресурсами. Для обычного прикладного Rust оно почти незаметно.

Новые проверки могут выявить проблемный низкоуровневый код

Rust 1.98 добавляет два новых lint, связанных с определениями runtime-символов.

invalid_runtime_symbol_definitions включён в режиме deny-by-default, а suspicious_runtime_symbol_definitions — warn-by-default. На первом этапе проверки ориентированы прежде всего на символы уровня core, включая функции наподобие memcmp, memset и strlen.

Цель — раньше обнаруживать опасные или некорректные определения символов, которые могут конфликтовать с ожиданиями компилятора и runtime-среды. В будущих версиях Rust область действия этих проверок планируется расширять.

Дополнительно появился warn-by-default lint c_void_returns, который проверяет использование core::ffi::c_void как возвращаемого типа.

Для большинства приложений эти изменения не потребуют правок. Зато разработчикам embedded-систем, собственных runtime, FFI-слоёв и no_std-окружений после обновления стоит внимательно посмотреть на новые предупреждения и ошибки компилятора.

Изменения платформенной поддержки

Rust 1.98 меняет статус нескольких target-платформ.

В Tier 2 переведены:

  • thumbv7a-none-eabi;
  • thumbv7a-none-eabihf;
  • thumbv7r-none-eabi;
  • thumbv7r-none-eabihf;
  • thumbv8r-none-eabihf.

Это улучшает официальный уровень поддержки ряда ARM-целей, актуальных для embedded-разработки.

Одновременно добавлены новые Tier 3 targets:

  • powerpc64-unknown-linux-gnuelfv2;
  • aarch64-unknown-linux-pauthtest.

Tier 2 означает более высокий уровень регулярного тестирования и поддержки со стороны проекта, тогда как Tier 3 в первую очередь фиксирует возможность сборки и наличие конфигурации target без тех же гарантий CI.

Совместимость — на что обратить внимание после обновления

Rust 1.98 содержит несколько изменений, способных проявиться в старом или нестандартном коде.

Среди них:

  • более строгая обработка неоднозначных импортов;
  • часть случаев ambiguous_glob_imports теперь превращается из lint в ошибку;
  • ужесточена проверка repr(transparent);
  • исправлена проверка равенства размеров при transmute() для некоторых комбинаций repr;
  • std::env::Vars и VarsOs больше не получают некорректные реализации Send/Sync;
  • на Windows деструкторы thread-local объектов переведены на Fiber Local Storage;
  • в Emscripten теперь безусловно используется WASM exception handling ABI;
  • unsafe_code lint более последовательно срабатывает для unsafe-атрибутов;
  • rustfmt теперь обнаруживает модули, объявленные через cfg_select!, поэтому после форматирования может затрагиваться больше файлов.

Большинство этих изменений исправляет ранее допускавшиеся пограничные или потенциально некорректные конструкции. Обычные приложения, использующие безопасный Rust и типичный набор crates, с высокой вероятностью обновятся без изменений исходного кода.

Библиотекам с большим количеством unsafe, собственными layout-гарантиями, сложными импортами, FFI и платформенно-зависимым кодом желательно прогнать полный CI до обновления минимальной поддерживаемой версии Rust.

Полные release notes Rust 1.98.0

Cargo 1.98 в основном исправляет регрессии и готовит будущие функции

В стабильной части Cargo 1.98 нет крупной новой пользовательской функции масштаба algebraic_* из стандартной библиотеки. Основное заметное исправление касается Windows: Cargo теперь удаляет завершающий carriage return из токенов, полученных через credential provider cargo:token-from-stdout.

Регрессия появилась в Cargo 1.96 и могла приводить к ошибке failed to parse header value при работе с токенами в Windows-окружении.

В nightly-части одновременно продолжается подготовка нескольких будущих возможностей, включая минимальный возраст публикации зависимости при разрешении версий, MSRV-aware diagnostics и JSON-вывод документации через cargo doc. Эти функции в Rust 1.98 остаются нестабильными и не должны восприниматься как часть стабильного интерфейса релиза.

Changelog Cargo 1.98

Rust 1.98 — кому стоит обновляться

Rust 1.98 — обычный стабильный релиз с заметными улучшениями стандартной библиотеки, а не экстренное обновление безопасности. Для большинства проектов переход можно проводить в штатном цикле после выполнения тестов и CI.

Наибольший практический интерес релиз представляет для трёх групп разработчиков.

Первая — авторы производительного численного кода. algebraic_* даёт возможность локально разрешить более агрессивные оптимизации floating-point вычислений, не переводя всю программу на ослабленную численную модель.

Вторая — разработчики библиотек сериализации, логирования, протоколов и инфраструктурного ПО. NumBuffer и format_into позволяют выполнять распространённую операцию преобразования целых чисел в текст средствами стандартной библиотеки с низкими накладными расходами.

Третья — авторы системного, embedded- и unsafe-кода. Новые lint, изменения repr(transparent), transmute(), runtime-символов и платформенной поддержки могут выявить конструкции, которые раньше компилировались, но находились за пределами желательной или корректной семантики.

Обновить установленный stable toolchain можно стандартной командой:

rustup update stable

Для приложений, где важна точная воспроизводимость вычислений с плавающей точкой, сами новые algebraic_* методы применять автоматически не стоит. Они являются явным выбором разработчика в пользу дополнительной свободы оптимизатора и потенциальной производительности ценой более слабых гарантий численного результата.

При использовании материалов сайта необходимо указывать ссылку на TGLand.ru. Если вы копируете фрагменты текста в интернете, прямая гиперссылка, доступная для индексации поисковыми системами, должна быть размещена в начале материала.

Вам также может понравиться