#prog #rust #rustreleasenotes
Вышла версия Rust 1.75.0! Как всегда, тут только избранные моменты, а все изменения отдельно (BTW ссылка теперь ведёт не RELEASES.md, а на отдельную страницу).
▪️Само значимое изменение: теперь возможно использовать impl Trait в возвращаемых типах методов трейта и, как следствие, эквивалентные методам с RPIT async-методы. Однако текущая реализация ИМЕЕТ СУЩЕСТВЕННЫЕ ОГРАНИЧЕНИЯ, так что это не рекомендуется к использованию для публичного API. Подробнее об ограничениях (и костылях) — тут.
А, и ещё RPIT в трейтах захватывает времена жизни иначе, чем RPIT в inherent методах и свободных функциях. Подробнее тут (осторожно, может вызвать головную боль). Этот способ планируют сделать для всех impl Trait типов в возвращаемых позициях в edition 2024.
▪️В const fn сейчас нельзя использовать &mut-ссылки. Проверка для этого в компиляторе была, однако, излишне строгой. Теперь её ослабили и в const fn можно манипулировать fn pointer-ами, у которых есть мутабельные ссылки в аргументах. Но вызывать их по прежнему нельзя.
▪️Ещё изменение касательно const fn: вычисления в них с нарушением выравнивания теперь приводят к ошибке компиляции. Раньше на это был deny default линт, который можно было явно отключить.
▪️И ещё касательно невыровненных ссылок: компилятор теперь корректно ловит создание ссылок на unsized поля в #[repr(packed)] структурах.
▪️В язык и std добавлено несколько новых гарантий, о которых я упоминал:
🔸char имеет одинаковый с u32 размер и выравнивание
🔸null всегда имеет нулевой адрес
🔸для некоторых типов Option<T> вызов transmute на памяти, забитой нулями, валиден и гарантированно даёт None
А также:
🔸задокументированно, когда атомарные load валидны на readonly памяти.
▪️match теперь может матчиться на usize/isize значениях с проверкой полноты покрытия без _, а также может матчиться по (частично) перемещённым значениям, если паттерны не смотрят на значение. Это поведение консистентно с поведением
▪️Метод
▪️Стабилизированы некоторые новые API, в частности:
🔸Конструкторы ссылок на Atomic-типы из сырых указателей (unsafe, разумеется)
🔸Пачку API (включая платформо-специфичные) для манипуляции временны́ми атрибутами файлов
🔸Битовые операции над IP-адресами
🔸Option::as{, _mut}_slice.
🔸Пачку методов для сырых указателей с указаниями смещений в байтах, а не в размерах указываемого типа.
Также в const контексте теперь можно вызывать:
🔸
▪️Некоторые улучшения rustdoc:
🔸Генерируется предупреждение, когда трейт не является object safe.
🔸Атрибут #[repr(transparent)] скрывается, если поле, над которым тип является transparent, является приватным (поскольку в этом случае repr наверняка является деталью реализации)
🔸Для C-like (с вариантами без полей) enum теперь показываются дискриминанты вариантов.
▪️Компилятор (конкретно librustc_driver) теперь оптимизируется при помощи BOLT — специального инструмента для оптимизации скомпилированных программ, который переставляет инструкции для лучшей утилизации кеша. Даёт прибавку в скорости.
▪️Маленькие функции теперь автоматически инлайнятся через границу крейтов. Дало большой выигрыш и по primary, и по secondary бенчмаркам — причём для времени компиляции даже лучше, чем для рантайм-бенчмарков.
Вышла версия Rust 1.75.0! Как всегда, тут только избранные моменты, а все изменения отдельно (BTW ссылка теперь ведёт не RELEASES.md, а на отдельную страницу).
▪️Само значимое изменение: теперь возможно использовать impl Trait в возвращаемых типах методов трейта и, как следствие, эквивалентные методам с RPIT async-методы. Однако текущая реализация ИМЕЕТ СУЩЕСТВЕННЫЕ ОГРАНИЧЕНИЯ, так что это не рекомендуется к использованию для публичного API. Подробнее об ограничениях (и костылях) — тут.
А, и ещё RPIT в трейтах захватывает времена жизни иначе, чем RPIT в inherent методах и свободных функциях. Подробнее тут (осторожно, может вызвать головную боль). Этот способ планируют сделать для всех impl Trait типов в возвращаемых позициях в edition 2024.
▪️В const fn сейчас нельзя использовать &mut-ссылки. Проверка для этого в компиляторе была, однако, излишне строгой. Теперь её ослабили и в const fn можно манипулировать fn pointer-ами, у которых есть мутабельные ссылки в аргументах. Но вызывать их по прежнему нельзя.
▪️Ещё изменение касательно const fn: вычисления в них с нарушением выравнивания теперь приводят к ошибке компиляции. Раньше на это был deny default линт, который можно было явно отключить.
▪️И ещё касательно невыровненных ссылок: компилятор теперь корректно ловит создание ссылок на unsized поля в #[repr(packed)] структурах.
▪️В язык и std добавлено несколько новых гарантий, о которых я упоминал:
🔸char имеет одинаковый с u32 размер и выравнивание
🔸null всегда имеет нулевой адрес
🔸для некоторых типов Option<T> вызов transmute на памяти, забитой нулями, валиден и гарантированно даёт None
А также:
🔸задокументированно, когда атомарные load валидны на readonly памяти.
▪️match теперь может матчиться на usize/isize значениях с проверкой полноты покрытия без _, а также может матчиться по (частично) перемещённым значениям, если паттерны не смотрят на значение. Это поведение консистентно с поведением
let _ = expr;.▪️Метод
std::split_inclusive теперь возвращает итератор, который корректно реализует DoubleEndedIterator в том плане, что возвращает одни и те же кусочки строки при итерации как в прямом, так и в обратном порядке.▪️Стабилизированы некоторые новые API, в частности:
🔸Конструкторы ссылок на Atomic-типы из сырых указателей (unsafe, разумеется)
🔸Пачку API (включая платформо-специфичные) для манипуляции временны́ми атрибутами файлов
🔸Битовые операции над IP-адресами
🔸Option::as{, _mut}_slice.
🔸Пачку методов для сырых указателей с указаниями смещений в байтах, а не в размерах указываемого типа.
Также в const контексте теперь можно вызывать:
🔸
MaybeUninit::assume_init_read🔸
MaybeUninit::zeroed🔸
mem::discriminant🔸
mem::zeroedНасчёт
Option::as_slice у вас мог возникнуть резонный вопрос, чем этот метод лучше opt_value.map_or(&[], std::slice::from_ref). Дело в том, что такой прямолинейный метод будет использовать некий посторонний адрес для случая, когда значение является None, а новый метод старается по возможности вернуть ссылку на слайс с адресом, совпадающим с адресом исходного опционального значения.▪️Некоторые улучшения rustdoc:
🔸Генерируется предупреждение, когда трейт не является object safe.
🔸Атрибут #[repr(transparent)] скрывается, если поле, над которым тип является transparent, является приватным (поскольку в этом случае repr наверняка является деталью реализации)
🔸Для C-like (с вариантами без полей) enum теперь показываются дискриминанты вариантов.
▪️Компилятор (конкретно librustc_driver) теперь оптимизируется при помощи BOLT — специального инструмента для оптимизации скомпилированных программ, который переставляет инструкции для лучшей утилизации кеша. Даёт прибавку в скорости.
▪️Маленькие функции теперь автоматически инлайнятся через границу крейтов. Дало большой выигрыш и по primary, и по secondary бенчмаркам — причём для времени компиляции даже лучше, чем для рантайм-бенчмарков.
🔥8❤🔥3👍2🎉1
#prog #rust #rustreleasenotes
Вышла версия Rust 1.76.0... Почти месяц назад. В свою защиту могу сказать, что изменения довольно минорные и потому релиз не обязывает к апдейту. Как обычно, тут только отдельные моменты, целиком в release notes.
▪️Единственная существенная вещь: исправлены ошибки при работе с unsized
▪️Задокументированы гарантии насчёт совместимости по ABI. Они были и раньше, просто теперь записаны.
▪️
▪️Исправлен старый баг с некорректным округлением чисел при форматировании в научной нотации с ограниченным числом десятичных знаков после запятой.
▪️Опция
▪️
▪️Реализации
▪️Как я уже писал,
▪️Пачка новых API:
🔸Option::inspect,
🔸
🔸type_name_of_val
🔸
🔸ptr::addr_eq — потому что сравнение указателей вместе с метаданными почти всегда не то, что нужно
🔸
Вышла версия Rust 1.76.0... Почти месяц назад. В свою защиту могу сказать, что изменения довольно минорные и потому релиз не обязывает к апдейту. Как обычно, тут только отдельные моменты, целиком в release notes.
▪️Единственная существенная вещь: исправлены ошибки при работе с unsized
#[repr(packed(N))] структурами, где N > 1. Именно — неправильный расчёт смещения до unsized поля и неправильный подсчёт размера и выравнивания в рантайме.▪️Задокументированы гарантии насчёт совместимости по ABI. Они были и раньше, просто теперь записаны.
▪️
dbg! теперь печатает и колонку места расположения▪️Исправлен старый баг с некорректным округлением чисел при форматировании в научной нотации с ограниченным числом десятичных знаков после запятой.
▪️Опция
create на File теперь работает корректно со скрытыми файлами на Windows вместо того, чтобы тихо падать.▪️
Vec::from_iter теперь переиспользует аллокацию с ещё несколькими итераторами, которые оборачивают vec::IntoIter.▪️Реализации
Debug для RwLockReadGuard и RwLockWriteGuard теперь не требуют Sized на типе внутри лока.▪️Как я уже писал,
IMPLIED_BOUNDS_ENTAILMENT теперь является ошибкой компиляции.▪️Пачка новых API:
🔸Option::inspect,
Result::{inspect, inspect_err} (с семантикой, аналогичной Iterator::inspect)🔸
{Arc, Rc}::unwrap_or_clone🔸type_name_of_val
🔸
ptr::{from_ref, from_mut}. Имеет смысл по тем же причинам, почему вместо as-кастов на числах используются from/into.🔸ptr::addr_eq — потому что сравнение указателей вместе с метаданными почти всегда не то, что нужно
🔸
std::hash::{DefaultHasher, RandomState}. Строго говоря, не новое API, но раньше это было доступно только через std::collections::hash_map.👍8❤1
#prog #rust #rustreleasenotes
Вышла версия Rust 1.77.0! Как всегда, тут только избранные хайлайты, а полные изменения в ченджлоге.
▪️Асинхронные функции теперь могут быть рекурсивными при условии наличии индирекции. Скажем, такой код по прежнему не работает:
, поскольку асинхронные функции компилируются в стейт-машины, размер которых должен быть известен на этапе компиляции, а размер стека функций явно зависит от рантайм-параметра. Компилятор на это выдаёт относительно хорошую ошибку:
(к сожалению, на сами рекурсивные вызовы не указывает)
Если воспользоваться предложением компилятора, то можно получить вот такой рабочий код:
Можно даже слегка угореть по уменьшению количества аллокаций и сделать так:
▪️В язык добавили строковые литералы типа
Очень удобная вещь для интеропа с C. Эти литералы могут быть сырыми (
▪️Добавили макрос
▪️Для слайсов добавили пачку методов, позволяющих доставать части слайса в виде ссылок на массивы:
🔸
🔸
🔸
🔸
Также добавили методы
▪️Опубликован крейт cargo-util-schemas, который является публичной схемой
▪️Rustdoc теперь поддерживает ссылки в заголовках и позволяет искать по типам, используя естественный синтаксис для кортежей (со скобочками).
Вышла версия Rust 1.77.0! Как всегда, тут только избранные хайлайты, а полные изменения в ченджлоге.
▪️Асинхронные функции теперь могут быть рекурсивными при условии наличии индирекции. Скажем, такой код по прежнему не работает:
async fn fib(n: u32) -> u32 {
match n {
0 | 1 => 1,
_ => fib(n-1).await + fib(n-2).await
}
}, поскольку асинхронные функции компилируются в стейт-машины, размер которых должен быть известен на этапе компиляции, а размер стека функций явно зависит от рантайм-параметра. Компилятор на это выдаёт относительно хорошую ошибку:
error[E0733]: recursion in an async fn requires boxing
--> src/lib.rs:1:1
|
1 | async fn fib(n: u32) -> u32 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future
(к сожалению, на сами рекурсивные вызовы не указывает)
Если воспользоваться предложением компилятора, то можно получить вот такой рабочий код:
async fn fib(n: u32) -> u32 {
match n {
0 | 1 => 1,
_ => Box::pin(fib(n-1)).await + Box::pin(fib(n-2)).await
}
}Можно даже слегка угореть по уменьшению количества аллокаций и сделать так:
_ => Box::pin(async move { fib(n-1).await + fib(n-2).await }).await▪️В язык добавили строковые литералы типа
&'static CStr:use core::ffi::CStr;
const _: &CStr = c"look mum, no trailing nul!";
Очень удобная вещь для интеропа с C. Эти литералы могут быть сырыми (
cr"it is not --> \n <-- a newline") и поддерживают escape-последовательности для UTF-8 code point и байтовых значений за пределами ASCII. Символы за пределами ASCII кодируются в том же виде, что и в UTF-8. ▪️Добавили макрос
std::mem::offset_of!. Пока что поддерживается только в форме смещения поля (полей) от начала раскладки структуры. Технически такой макрос возможно реализовать и сейчас, но для того, чтобы это сделать правильно (без UB), нужно некоторое количество не самого тривиального unsafe кода.▪️Для слайсов добавили пачку методов, позволяющих доставать части слайса в виде ссылок на массивы:
🔸
slice::first_chunk{, _mut}🔸
slice::split_first_chunk{, _mut}🔸
slice::last_chunk{, _mut}🔸
slice::split_last_chunk{, _mut}Также добавили методы
slice::chunk_by{, _mut}, который возвращает итератор из частей слайсов, в которых последовательные пары элементов удовлетворяют предоставленному предикату:let slice = &[1, 1, 1, 3, 3, 2, 2, 2];
let mut iter = slice.chunk_by(|a, b| a == b);
assert_eq!(iter.next(), Some(&[1, 1, 1][..]));
assert_eq!(iter.next(), Some(&[3, 3][..]));
assert_eq!(iter.next(), Some(&[2, 2, 2][..]));
assert_eq!(iter.next(), None);
▪️Опубликован крейт cargo-util-schemas, который является публичной схемой
Cargo.toml , используемой в самом cargo.▪️Rustdoc теперь поддерживает ссылки в заголовках и позволяет искать по типам, используя естественный синтаксис для кортежей (со скобочками).
👍10
#prog #rust #rustreleasenotes
Вышла версия Rust 1.78.0! Как всегда, тут только то, что показалось мне интересным, а всё остальное в детальных заметках о релизе.
▪️Появилось новое пространство имён для атрибутов:
▪️В std при
▪️Некоторые из функций на указателях стали более полезными, поскольку теперь обещают более строгие результаты.
Обе оговорки были связаны с применением этих функций в const-контексте. Сейчас их убрали, поскольку они и сейчас нестабильны в const-контекстах.
▪️Реализация
▪️В метод
▪️В паттернах теперь нельзя использовать константы типов, не реализующих PartialEq и NaN.
▪️Компилятор теперь по умолчанию не компилирует код с неверными
▪️Компилятор теперь детектирует больше избыточных импортов. Это изменение позволило убрать лишние импорты и в самом компиляторе во многих местах.
▪️Компилятор теперь предлагает переместить
▪️Компилятор теперь диагностирует каст ссылки из одного типа в другой с бо́льшим размером.
Вышла версия Rust 1.78.0! Как всегда, тут только то, что показалось мне интересным, а всё остальное в детальных заметках о релизе.
▪️Появилось новое пространство имён для атрибутов:
diagnostic. В настоящий момент там только один атрибут: on_unimplemented (о котором я рассказывал). Он позволяет кастомизировать сообщение, выдаваемое компилятором для случаев, когда обобщённому коду, который требует этот трейт на обобщённом параметре, предоставляется тип, не реализующий этот трейт. Это уже используется в axum.▪️В std при
debug_assertions теперь проверяются некоторые из предусловий на unsafe функциях. Раньше это было невозможно из-за того, что std всегда поставлялась в релизной сборке.▪️Некоторые из функций на указателях стали более полезными, поскольку теперь обещают более строгие результаты.
🔸pointer::align_offset возвращает смещение, необходимое для того, чтобы выровнять указатель до указанного выравнивания, или usize::MAX, если это невозможно. Раньше ей разрешалось всегда возвращать usize::MAX.🔸slice::align_to и slice::align_to_mut переводят &{mut} [T] в (&{mut} [T], &{mut} [U], &{mut} [T]), где слайс в середине теперь максимально возможного размера с учётом ограничений на выравнивание и размер. Раньше функциям разрешалось возвращать, скажем, исходный слайс целиком как первый элемент тройки.Обе оговорки были связаны с применением этих функций в const-контексте. Сейчас их убрали, поскольку они и сейчас нестабильны в const-контекстах.
▪️async-методы теперь могут возвращать в реализациях конкретные типы, реализующие Future (а не только impl Future).▪️Реализация
RwLock теперь полностью кастомная и не зависит от pthread. Это позволяет оградиться от багов на старых системах, а также избежать аллокаций (т. к. примитивы синхронизации pthread неперемещаемы) и повысить производительность.▪️В метод
char::is_grapheme_extended добавлена проверка на ASCII, чтобы избежать лукапа по юникодным таблицам (последовавший за ним PR переместил эту проверку в код, генерируемый по таблицам Unicode). Звучит, как что-то узкоспециализированное, но этот метод в конечном счёте вызывается в реализации Debug для str. Как следствие, это изменение более чем вдвое ускорило дерайв Debug!▪️В паттернах теперь нельзя использовать константы типов, не реализующих PartialEq и NaN.
▪️Компилятор теперь по умолчанию не компилирует код с неверными
#[doc]-атрибутами.▪️Компилятор теперь детектирует больше избыточных импортов. Это изменение позволило убрать лишние импорты и в самом компиляторе во многих местах.
▪️Компилятор теперь предлагает переместить
macro_rules! выше по тексту, если декларативный макрос вызывается раньше, чем определяется в этом файле.▪️Компилятор теперь диагностирует каст ссылки из одного типа в другой с бо́льшим размером.
👍8🔥3
#prog #rust #rustreleasenotes
Вышла версия Rust 1.79.0! Как обычно, тут только избранные моменты, остальное в полных заметках.
▪️Стабилизированы inline const блоки! Одно из самых непосредственно полезных применений — array repeat expression. Если раньше приходилось писать что-то в духе:
, то теперь можно писать просто
(конечно, при условии, что вывод типов поймёт конкретный тип
Ещё одно полезное применение — статические ассерты. Вместо того, чтобы выписывать
Это тоже работает в обобщённых контекстах, но по очевидным причинам при зависимости от обобщённых параметров может выстрелить только при мономорфизации:
К сожалению, сейчас у этой фичи есть ограничения. Именно,
Ещё одна особенность — если
▪️Теперь можно писать ограничения на ассоциированный тип прямо в баундах:
▪️Продление времени жизни ссылок на временные значения теперь работает и при возврате из
▪️main теперь может быть реэкспортом, а не определена непосредственно в корне иерархии:
Так можно использовать и функцию из другого крейта.
▪️Команды cargo теперь по умолчанию учитывают MSRV (minimal supported Rust version) при выборе версий зависимостей.
▪️rustdoc теперь показывает имена в результатах поиска один раз, даже если они доступны по нескольким путям из-за реэкспортов.
▪️У трейта
Вышла версия Rust 1.79.0! Как обычно, тут только избранные моменты, остальное в полных заметках.
▪️Стабилизированы inline const блоки! Одно из самых непосредственно полезных применений — array repeat expression. Если раньше приходилось писать что-то в духе:
const EMPTY: Option<Vec<u8>> = None;
let foo = [EMPTY; 100];
, то теперь можно писать просто
let foo = [const { None }; 100];(конечно, при условии, что вывод типов поймёт конкретный тип
Option внутри массива). Более того, в обобщённых контекстах это тоже работает:fn create_none_array<T, const N: usize>() -> [Option<T>; N] {
[const { None }; N]
}Ещё одно полезное применение — статические ассерты. Вместо того, чтобы выписывать
_-константы, можно просто окружить ассерт const {}:fn broken() {
const { assert!(2 + 2 == 5) } // ошибка компиляции
}Это тоже работает в обобщённых контекстах, но по очевидным причинам при зависимости от обобщённых параметров может выстрелить только при мономорфизации:
use std::mem::size_of;
const fn foo<T>() -> usize {
const { assert!(size_of::<T>() > 1) }
size_of::<T>() - 1
}
fn main() {
_ = foo::<i32>();
_ = foo::<u8>(); // ошибка компиляции
}
К сожалению, сейчас у этой фичи есть ограничения. Именно,
const-блоки можно использовать только в выражениях, но нельзя, в отличие от обычных констант, использовать их в паттернах (на это сейчас есть отдельная фича). Так что следующий код, к сожалению, пока что требует nightly (и #![feature(inline_const_pat)]):enum Enum {
Foo,
Bar,
Baz,
}
impl Enum {
fn from_usize(n: usize) -> Result<Self, usize> {
use Enum::{Foo, Bar, Baz};
Ok(match n {
const { Foo as _ } => Foo,
const { Bar as _ } => Bar,
const { Baz as _ } => Baz,
_ => return Err(n),
})
}
}Ещё одна особенность — если
const-блок находится в статически недостижимом коде, то он может быть не вычислен и, соответственно, не вызвать ошибку компиляции на ошибочной константе.▪️Теперь можно писать ограничения на ассоциированный тип прямо в баундах:
T: Trait<Assoc: Bounds...>. В stabilization report рассказывается более подробно, чем это полезно. Одно из применений — возможность указывать ограничения на ассоциированных типах на неназываемых impl Trait типах.▪️Продление времени жизни ссылок на временные значения теперь работает и при возврате из
if и match. Этот код компилируется сейчас и не компилировался раньше:let a = String::from("a");
let _ = if true { &a } else { &String::from("b") };
let _ = match () {
() => &String::from("c"),
};▪️main теперь может быть реэкспортом, а не определена непосредственно в корне иерархии:
mod foo {
pub(crate) fn bar() {}
}
use foo::bar as main;Так можно использовать и функцию из другого крейта.
▪️Команды cargo теперь по умолчанию учитывают MSRV (minimal supported Rust version) при выборе версий зависимостей.
▪️rustdoc теперь показывает имена в результатах поиска один раз, даже если они доступны по нескольким путям из-за реэкспортов.
▪️У трейта
Clone есть метод clone_from с сигнатурой fn(&mut self, source: &Self), который позволяет перезаписать содержимое self напрямую вместо того, чтобы замещать self полностью свежей копией. Однако это только возможность, и большинство типов используют реализацию по умолчанию: *self = source.clone(). Более того, для некоторых типов переопределение этого метода может иметь наблюдаемое изменение поведения по сравнению с реализацией по умолчанию, даже если clone_from не имеет намеренно отличную семантику. По этой причине std теперь документирует переопределённый метод clone_from на коллекциях.🔥3
#prog #rust #rustreleasenotes
Вышла версия Rust 1.80.0! Как всегда, тут только избранные моменты, а остальное в детальных заметках о релизе.
(rust playground всё ещё не обновился, так что примеры кода проверяйте локально или на nightly)
▪️В паттернах теперь можно использовать диапазоны с исключающей верхней границей. Пример из блога:
▪️Важное изменение для unsafe кода: теперь на произвольных выровненных указателях разрешены чтение и запись значений типов нулевого размера, включая null. Также на всех указателях (и null) разрешены смещения на 0, и offset_from всегда разрешён для указателей с одинаковым адресом из одной аллокации.
▪️В релизе Rust 1.75.0 были даны гарантии на раскладку в памяти
Например,
⚠️ std теперь может уронить программу в большем числе случаев. Смотри ниже:
🔸Помните, в Rust 1.63 добавили типы для IO safety? Так вот, в отладочной сборке (читай, при
🔸Также в отладочной сборке
🔸Тип fmt::Error должен использоваться лишь для индикации того, что запись в нижележащий приёмник информации более невозможна, но не для передачи ошибок в самом процессе форматирования как таковом. В частности, в силу того, что
🔸PathBuf::set_extension теперь паникует, если новое расширение содержит разделитель пути файловой системы.
▪️Добавлены типы LazyCell и LazyLock, которыеимитируют автора этого канала позволяют хранить значение, вычисляемое по требованию при помощи предоставленной при создании функции. Значит ли это, что теперь можно выкинуть once_cell? Отнюдь. У этих типов не стабилизированы методы для получения содержимого без форсирования их вычисления, и по непонятным причинам оба не дают никакого способа (даже нестабильного) получить мутабельный доступ к содержимому.
Более того, получения значения из
Вышла версия Rust 1.80.0! Как всегда, тут только избранные моменты, а остальное в детальных заметках о релизе.
(rust playground всё ещё не обновился, так что примеры кода проверяйте локально или на nightly)
▪️В паттернах теперь можно использовать диапазоны с исключающей верхней границей. Пример из блога:
fn size_prefix(n: u32) -> &'static str {
const K: u32 = 10u32.pow(3);
const M: u32 = 10u32.pow(6);
const G: u32 = 10u32.pow(9);
match n {
..K => "",
K..M => "k",
M..G => "M",
G.. => "G",
}
}▪️Важное изменение для unsafe кода: теперь на произвольных выровненных указателях разрешены чтение и запись значений типов нулевого размера, включая null. Также на всех указателях (и null) разрешены смещения на 0, и offset_from всегда разрешён для указателей с одинаковым адресом из одной аллокации.
▪️В релизе Rust 1.75.0 были даны гарантии на раскладку в памяти
Option для некоторых типов т. н. "null pointer optimization" (название не вполне верное, поскольку не только у ссылок есть ниша на битовом паттерне из одних нулевых битов). В этом релизе схожие гарантии дали и для раскладки Result. Именно, Result имеет такое же представление в памяти, как и аналогичный Option с одним из типовых параметров, если для этого параметра есть гарантии "null pointer optimization" при оборачивании в Option, а второй типовый параметр является типом нулевого размера с выравниванием 1, без полей и без атрибута #[non_exhaustive].Например,
Result<NonZero<i32>, ()> и Result<(), NonZero<i32>> теперь имеют такие же размер, выравнивание и гарантии ABI, как и Option<NonZero<i32>>.⚠️ std теперь может уронить программу в большем числе случаев. Смотри ниже:
🔸Помните, в Rust 1.63 добавили типы для IO safety? Так вот, в отладочной сборке (читай, при
#[cfg(debug_assertions)]) дроп OwnedFd теперь прерывает работу процесса, если соответствующий файловый дескриптор уже закрыт.🔸Также в отладочной сборке
unchecked_* арифметические методы паникуют при нарушении предусловий их безопасного использования.🔸Тип fmt::Error должен использоваться лишь для индикации того, что запись в нижележащий приёмник информации более невозможна, но не для передачи ошибок в самом процессе форматирования как таковом. В частности, в силу того, что
String может расширяться по мере нужды, форматирование в String считается операцией, которая не может вернуть ошибку. Для того, чтобы усилить соблюдение этого контракта, теперь форматирование в новый String паникует при ошибке из реализации одного из fmt методов, а форматирование в IO-ресурсы (стандартный вывод, например) паникует, если ошибку вернул метод форматирования, но не запись данных в IO-ресурс.🔸PathBuf::set_extension теперь паникует, если новое расширение содержит разделитель пути файловой системы.
▪️Добавлены типы LazyCell и LazyLock, которые
Более того, получения значения из
LazyLock блокирует вызывающий поток, если значение ещё не вычислено или в процессе вычисления. Как правило, это то, что нужно для корректной логики, но в некоторых случаях можно или даже нужно позволить сразу нескольким потокам вычислять значение одновременно и установить в итоге то, которое было у самого первого потока, завершившего работу (например, время старта первого рабочего потока в тредпуле). Для таких юзкейсов в once_cell есть типы из модуля race. Помимо логического отличия, эти типы за счёт отказа от блокировки потоков могут быть использованы на платформах без операционной системы.👍8😁1
#prog #rust #rustreleasenotes
Вышла версия Rust 1.81.0! Как всегда,котлеты детальные заметки о релизе отдельно, а мухи мои хайлайты отдельно.
▪️Компилятор теперь несколько иначе выводит лайфтаймы. Раньше при наличии
▪️Ещё насчёт лайфтаймов: компилятор теперь по умолчанию не компилирует код с ассоциированными константами с лайфтаймами в типах, если есть именованные лайфтаймы в текущей области видимости:
▪️Стабилизировали атрибут
Эта вещь особенно полезна при написании чернового варианта кода. В отличие от
▪️Ещё изменение касательно атрибутов для линтов: если подобный линт вызывает предупреждение компилятора, то оно теперь непосредственно включает сообщение из
Вывод:
Строка
▪️У большинства вариантов ABI в Rust есть
▪️
▪️
▪️До стейбла дошли улучшения сортировок. Да, теперь ваш говнокод в реализации
Вышла версия Rust 1.81.0! Как всегда,
▪️Компилятор теперь несколько иначе выводит лайфтаймы. Раньше при наличии
self компилятор выводил время жизни из ссылки на Self, если оно было. Теперь компилятор ищет ссылку не непосредственно на Self, а на тип, включающий в себя Self, и прерывает компиляцию, если в типе self больше одного лайфтайма. Примеры из PR:// ранее компилировалось, а теперь нет
fn a(self: &Box<&Self>) -> &u32
fn b(self: &Pin<&mut Self>) -> &String
fn c(self: &mut &Self) -> Option<&Self>
fn d(self: &mut &Box<Self>, arg: &usize) -> &usize
// ранее не компилировалось, теперь компилируется
fn f(self: &Box<Self>) -> &u32
// компилируется, но теперь выбирает иной лайфтайм
fn e(self: &mut Box<Self>, arg: &usize) -> &usize
// ^ теперь ^ раньше
▪️Ещё насчёт лайфтаймов: компилятор теперь по умолчанию не компилирует код с ассоциированными константами с лайфтаймами в типах, если есть именованные лайфтаймы в текущей области видимости:
struct Foo<T>(T);
impl<'a> Foo<&'a ()> {
const STR: &str = "hello, world"; // низзя (но можно #[allow], если хочется)
}
impl Foo<()> {
const STR: &str = "hello, world"; // можно, других лт нет, выводится 'static
}
▪️Стабилизировали атрибут
#[expect], который заставляет компилятор выдавать предупреждение, если указанный линт не срабатывает. Пример:fn consume(_: i32) {
todo!()
}
pub fn foo() {
let mut x = 0; // warning: variable does not need to be mutable
consume(x);
}
pub fn bar() {
#[expect(unused_mut)]
let mut x = 0; // нет предупреждений
consume(x);
}Эта вещь особенно полезна при написании чернового варианта кода. В отличие от
#[allow], компилятор будет выдавать предупреждение, когда весь код, вызывающий срабатывание указанного линта, будет исправлен.▪️Ещё изменение касательно атрибутов для линтов: если подобный линт вызывает предупреждение компилятора, то оно теперь непосредственно включает сообщение из
reason:fn consume(_: i32) {}
#[deny(unused_mut, reason = "mutability is evil")]
pub fn f() {
let mut x = 0;
consume(x);
}Вывод:
error: variable does not need to be mutable
--> src/lib.rs:5:9
|
5 | let mut x = 0;
| ----^
| |
| help: remove this `mut`
|
= note: mutability is evil
note: the lint level is defined here
--> src/lib.rs:3:8
|
3 | #[deny(unused_mut, reason = "mutability is evil")]
Строка
= note: mutability is evil ранее отсутствовала в выводе.▪️У большинства вариантов ABI в Rust есть
*-unwind варианты, которые позволяют паникам пересекать границу FFI. Для вариантов ABI без *-unwind такое поведение является UB. В этой версии Rust программа теперь прекращает работу (abort), когда паника пересекает границу ABI, не поддерживающего раскрутку стека.▪️
offset_from теперь можно всегда применять на указателях с одинаковыми адресами, вне зависимости от их происхождения (provenance). Да, в том числе на указателях из разных аллокаций. Сделано это для достижения provenance monotonicity: добавления произвольного provenance указателям не может добавить UB в программу, в которой UB не было.▪️
{Rc,Arc}::make_mut() теперь работают на слайсах из клонируемых значений. Реализовано через отдельный нестабильный трейт, так что для своих типов реализовать не получится, увы.▪️До стейбла дошли улучшения сортировок. Да, теперь ваш говнокод в реализации
Ord может вызвать панику.👍3
#prog #rust #rustreleasenotes
Вышла версия Rust 1.82.0! Как всегда, тут только избранные части, а полный ченджлог отдельно.
▪️Теперь в некоторых случаях можно использовать pattern matching, не упоминая ненаселённые паттерны:
▪️Добавили новый синтаксис, который позволяет явно указать, какие лайфтайм-параметры будут использованы для обобщённых параметров типа
▪️Стабилизировали операции на числами с плавающей точкой в const fn. В качестве не вполне приятного дополнения const fn теперь могут возвращать разные результаты в о время компиляции и в рантайме. С другой стороны, в настоящий момент эта оговорка существует лишь из-за тонкостей насчёт гарантий битового представления NaN. Если вас это не интересует (а так оно, скорее всего, и есть), то const fn продолжают сохранять равенство рантайм и компилтайм поведения.
▪️Стабилизировали операции для взятия адреса place expression без создания промежуточной ссылки:
▪️В Rust добавили ключевое слово
▪️Макрос
▪️Некоторые атрибуты — например,
С edition 2024 (да, ещё не стабилизированной) подобные атрибуты нужно будет помечать, как
В предыдущих edition по соображениям обратной совместимости это не требуется, но есть соответствующий линт (по умолчанию выключенный).
⬇️⬇️⬇️
Вышла версия Rust 1.82.0! Как всегда, тут только избранные части, а полный ченджлог отдельно.
▪️Теперь в некоторых случаях можно использовать pattern matching, не упоминая ненаселённые паттерны:
enum Void {}
fn foo() -> Result<u32, Void> {
todo!()
}
fn main() {
let Ok(x) = foo();
match foo() {
Ok(x) => {}
}
}▪️Добавили новый синтаксис, который позволяет явно указать, какие лайфтайм-параметры будут использованы для обобщённых параметров типа
impl Trait в возвращаемой позиции: + use<'a>. Это позволяет, в частности, убрать из списка лайфтайм-параметров те, которые по факту не используются в возвращаемом типе. У меня не получилось кратко объяснить, для чего это нужно, читайте блогпост. Из досадных ограничений: анонимный тип обязан перечислять в use все обобщённые типы и константные параметры из окружения, даже если они не используются. Это планируют исправить в будущих версиях.▪️Стабилизировали операции на числами с плавающей точкой в const fn. В качестве не вполне приятного дополнения const fn теперь могут возвращать разные результаты в о время компиляции и в рантайме. С другой стороны, в настоящий момент эта оговорка существует лишь из-за тонкостей насчёт гарантий битового представления NaN. Если вас это не интересует (а так оно, скорее всего, и есть), то const fn продолжают сохранять равенство рантайм и компилтайм поведения.
▪️Стабилизировали операции для взятия адреса place expression без создания промежуточной ссылки:
&raw const и &raw mut, ранее доступные через макросы std::ptr::addr_of{, _mut}. Также в качестве багфикса их использование на static определениях теперь не триггерит сообщение о небезопасной операции.▪️В Rust добавили ключевое слово
safe... Ну, почти. extern-определения по определению не могут быть проверены компилятором на корректность, поэтому в общем случае доступ к ним требует unsafe-блока. Начиная с этой версии определение в extern-блоке можно пометить, как safe, обозначая тем самым, что доступ к определению не требует соблюдения каких-либо специальных предусловий. Например, таковыми можно пометить большинство функций из libm — математической составляющей стандартной библиотеки C.▪️Макрос
offset_of! теперь можно использовать для вычисления сдвига вложенного поля:struct Inner {
field: u32,
}
struct Outer {
inner: Inner,
}
const _: usize = std::mem::offset_of!(Outer, inner.field);▪️Некоторые атрибуты — например,
#[no_mangle] и #[export_name] — могут приводить к некорректным программаv даже в отсутствие какого-либо unsafe кода. Например, вот эта простая программа при запуске на *nix-системе почти наверняка упадёт с SIGSEGV:fn main() {
println!("Hello, world!");
}
#[no_mangle]
fn malloc() -> usize { 1 }С edition 2024 (да, ещё не стабилизированной) подобные атрибуты нужно будет помечать, как
unsafe:#[unsafe(no_mangle)]
fn this_is_not_in_global_namespace() {}
В предыдущих edition по соображениям обратной совместимости это не требуется, но есть соответствующий линт (по умолчанию выключенный).
⬇️⬇️⬇️
👍7🔥6🥰1😍1
#prog #rust #rustreleasenotes
Вышла версия Rust 1.83.0! Как всегда, тут только избранные части, а всё остальное в детальных заметках.
▪️В const-контекстах теперь можно использовать
Написание const-кода серьёзно упростилось. Само итоговое значение константы, впрочем, не может быть изменяемой (в том числе через внутреннюю изменяемость) ссылкой.
Также итоговое значение константы теперь может содержать
По понятным причинам итоговое значение константы не может содержать изменяемую (в том числе через внутреннюю изменяемость) ссылку на
▪️Как прямое следствие предыдущего пункта — кучу функций можно теперь использовать в const-контексте, включая множество операций над сырыми указателями (в том числе
▪️Также стабилизировали кучу новых вариантов io::ErrorKind. Забавный факт: эти варианты были и раньше и использовались в std, и в теории по ним можно было матчиться, используя реализацию
▪️Ещё в число стабилизированных API вошли
▪️Одновременные атомарные и неатомарные доступы на чтение из одной локации в памяти одного размера теперь не считаются гонкой данных и потому не являются UB (в C++ — является, поскольку атомарные доступы в C++ происходят через создание atomic_ref, абстрактная машина C++ оперирует типизированной памятью).
▪️Предыдущие версии языка ошибочно считали
следующая функция скомпилируется вне зависимости от того, определена ли она в том же крейте, что и
▪️Из забавного: компилятор теперь выдаёт ошибку на атрибут
Вышла версия Rust 1.83.0! Как всегда, тут только избранные части, а всё остальное в детальных заметках.
▪️В const-контекстах теперь можно использовать
&mut-ссылки, а также &-ссылки на типы с внутренней изменяемостью!const fn inc(x: &mut i32) {
*x += 1;
}
const C: i32 = {
let mut c = 41;
inc(&mut c);
c
};Написание const-кода серьёзно упростилось. Само итоговое значение константы, впрочем, не может быть изменяемой (в том числе через внутреннюю изменяемость) ссылкой.
Также итоговое значение константы теперь может содержать
&-ссылку на static, если в типе нет внутренней изменяемости. static S: i32 = 25;
const C: &i32 = &S;
По понятным причинам итоговое значение константы не может содержать изменяемую (в том числе через внутреннюю изменяемость) ссылку на
static.▪️Как прямое следствие предыдущего пункта — кучу функций можно теперь использовать в const-контексте, включая множество операций над сырыми указателями (в том числе
NonNull) и различные функции для разбиения мутабельных слайсов на части.▪️Также стабилизировали кучу новых вариантов io::ErrorKind. Забавный факт: эти варианты были и раньше и использовались в std, и в теории по ним можно было матчиться, используя реализацию
Display. Ещё забавный факт: стабилизацию всех этих вариантов и ещё некоторых запланировали 23 месяца назад, но первый PR со стабилизацией застопорился из-за некоторых вариантов, которые были слишком специфичны для конкретных операционных систем. Собственно, новый PR со стабилизацией приняли лишь потому, что эти варианты из списка на стабилизацию выкинули.▪️Ещё в число стабилизированных API вошли
hash_map::{Entry, VacantEntry}::insert_entry. Эти методы вставляют переданное значение, но, в отличие от insert, возвращают не мутабельную ссылку на значение, а OccupiedEntry.▪️Одновременные атомарные и неатомарные доступы на чтение из одной локации в памяти одного размера теперь не считаются гонкой данных и потому не являются UB (в C++ — является, поскольку атомарные доступы в C++ происходят через создание atomic_ref, абстрактная машина C++ оперирует типизированной памятью).
▪️Предыдущие версии языка ошибочно считали
#[non_exhaustive]-структуры с ненаселёнными полями населёнными типами вне крейта, в которых они определены. В этой версии это странное поведение исправили. Для пользователей языка это означает, что, например, с вот таким определением:enum Empty {}
#[non_exhaustive]
struct Foo(Empty);следующая функция скомпилируется вне зависимости от того, определена ли она в том же крейте, что и
Foo, или нет:fn eliminate<T>(f: Foo) -> T {
match f {}
}▪️Из забавного: компилятор теперь выдаёт ошибку на атрибут
#[repr(Rust)], применённый на определение, которое не является перечислением, структурой или объединением.👍8❤4🎉2
#prog #rust #rustreleasenotes
Вышла версия Rust 1.84.0! Как всегда, тут только некоторые вещи, а остальное в полных заметках о релизе.
▪️Когерентность — свойство системы типов Rust: для любой наперёд заданной пары трейта и типа существует не более одного impl-а этого трейта для этого типа. Очень важное свойство, которое позволяет генерировать код и не беспокоится, что один и тот же метод будет означать разные вещи в разных местах кодовой базы. Новыйпорешатель трейтов trait solver (нет, не chalk, хотя и вдохновлён им) теперь используется для проверки когерентности. С точки зрения программиста это означает, что компилятор теперь корректно находит больше перекрывающихся impl-ов и корректно различает больше impl-ов, которые точно не перекрываются. Если вы пытались соорудить какую-то type-level фигню, которая не работала из-за
▪️
▪️У трейт-объектов, составленных из
▪️Я уже ссылался на статью про различие place expression. Так как непосредственно разыменовывание указателя не является UB — UB может являться последующая конвертация из place expression в value expression — взятие
▪️Косвенно связанное изменение — добавили линт на образование сырого указателя, который немедленно становится висячим из-за того, что указывает на дропнутое временное значение. Это расширило предыдущий аналогичный линт, который предупреждал о сыром указателе из временной
▪️Как я уже ссылался, касты между указателями и числами имеют на удивлению неоднозначную семантику — главным образом потому, что для корректной оптимизации кода с указателями надо знать их происхождение (provenance), а эта информация при касте из указателя в число теряется, и её потом неоткуда взять при обратном касте из числа в указатель. В стандартную библиотеку добавили API для манипуляции с адресами указателей, которые более явны насчёт того, что при этом происходит с provenance. Подробнее в документации на указатели.
▪️На примитивных численных типах стабилизировали метод isqrt, возвращающий округлённый вниз квадратный корень. Для знаковых чисел этот метод паникует на негативных значениях. Для обработки этой ситуации можно использовать checked_isqrt, который возвращает
▪️cargo теперь может выбирать версии зависимостей, принимая во внимание их MSRV (minimum supported Rust version). Это opt-in, но будет использоваться по умолчанию в третьей версии резолвера зависимостей, которая будет использоваться по умолчанию в edition 2024 (стабилизация в следующей версии!)
Вышла версия Rust 1.84.0! Как всегда, тут только некоторые вещи, а остальное в полных заметках о релизе.
▪️Когерентность — свойство системы типов Rust: для любой наперёд заданной пары трейта и типа существует не более одного impl-а этого трейта для этого типа. Очень важное свойство, которое позволяет генерировать код и не беспокоится, что один и тот же метод будет означать разные вещи в разных местах кодовой базы. Новый
conflicting implementations..., хотя вроде как должна — попробуйте сейчас. И да, это означает, что ваш код, вероятно, компилировался по недосмотру из-за бага компилятора, как derive-visitor (не волнуйтесь, там код уже поправили).▪️
#[deny(lint_name)] внутри #[forbid(lint_name)] теперь работает и продолжает запрещать lint_name, и нет, это не позволяет переопределить forbid. Само по себе не очень полезно, но это изменение хорошо для макросов, которые генерирую код с #[deny] в контексте, где действует #[forbid]. Предупреждения на это не выдаётся (к моему неудовольствию).▪️У трейт-объектов, составленных из
Trait + AutoTrait, при приведении теперь можно отбрасывать части, которые не соответствуют авто-трейтам. Например, теперь компилируется этот код:trait Trait {}
fn upcast(x: &(dyn Trait + Send)) -> &dyn Send { x }▪️Я уже ссылался на статью про различие place expression. Так как непосредственно разыменовывание указателя не является UB — UB может являться последующая конвертация из place expression в value expression — взятие
&raw {, const} (или, аналогично, addr_of{, _mut}!) от результата разыменовывания сырого указателя всегда является безопасной операцией. Да, даже если указатель null. Взятие адреса от доступа к полю place expression остаётся в общем случае unsafe, поскольку это требует, чтобы указатель был в пределах выделенной под значения указываемого типа памяти.▪️Косвенно связанное изменение — добавили линт на образование сырого указателя, который немедленно становится висячим из-за того, что указывает на дропнутое временное значение. Это расширило предыдущий аналогичный линт, который предупреждал о сыром указателе из временной
CString. В коде отмечается, что у линта пока есть false negative, так что целиком полагаться на это пока не стоит. С другой стороны, там же указаны конкретные операции, которые пока не проверяются, так что стоит ожидать увеличения полноты этого линта в будущем.▪️Как я уже ссылался, касты между указателями и числами имеют на удивлению неоднозначную семантику — главным образом потому, что для корректной оптимизации кода с указателями надо знать их происхождение (provenance), а эта информация при касте из указателя в число теряется, и её потом неоткуда взять при обратном касте из числа в указатель. В стандартную библиотеку добавили API для манипуляции с адресами указателей, которые более явны насчёт того, что при этом происходит с provenance. Подробнее в документации на указатели.
▪️На примитивных численных типах стабилизировали метод isqrt, возвращающий округлённый вниз квадратный корень. Для знаковых чисел этот метод паникует на негативных значениях. Для обработки этой ситуации можно использовать checked_isqrt, который возвращает
None для негативных значений. У NonZero этот метод тоже есть, но почему-то только для беззнаковых оборачиваемых типов.▪️cargo теперь может выбирать версии зависимостей, принимая во внимание их MSRV (minimum supported Rust version). Это opt-in, но будет использоваться по умолчанию в третьей версии резолвера зависимостей, которая будет использоваться по умолчанию в edition 2024 (стабилизация в следующей версии!)
👍9❤3🤯1