#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
#prog #rust #rustreleasenotes
(да, я пишу с опозданием почти в неделю, и что?)
Вышла версия Rust 1.85.0! Как всегда, тут только интересные мне части, остальное в полных заметках о релизе.
▪️Релизнули новую редакцию Rust 2024 edition! К сожалению, новая версия диапазонов не попала в эту версию.
🔸в
🔸также временные значения из последнего выражения в блоке теперь дропаются в конце блока, а не продолжают жить после него. Без этого изменения некоторый вполне нормально выглядящий код не компилировался:
🔸запрещены некоторые паттерны, которые крайне неочевидным образом взаимодействуют с pattern match ergonomics. Посмотрите по ссылке, там действительно странные вещи.
🔸
🔸cargo теперь по умолчанию учитывает MSRV при выборе версии зависимости.
Пачка изменений, связанных с unsafe кодом:
🔸
🔸атрибуты
🔸unsafe_op_in_unsafe_fn warn по умолчанию.
🔸ссылки на
🔸если компилятор не может вывести тип, к которому нужно привести выражение типа
▪️Теперь замыкания могут быть
В отличие от замыканий, возвращающих
▪️
▪️Для беззнаковых чисел, соответствующих
▪️В
▪️
(да, я пишу с опозданием почти в неделю, и что?)
Вышла версия Rust 1.85.0! Как всегда, тут только интересные мне части, остальное в полных заметках о релизе.
▪️Релизнули новую редакцию Rust 2024 edition! К сожалению, новая версия диапазонов не попала в эту версию.
🔸в
if let
значение, по которому идёт сопоставление с образцом, теперь дропается перед входом в else ветку вместо после конца if let. Это позволило, в частности, избежать багов, когда guard из примитива синхронизации живёт дольше необходимого.🔸также временные значения из последнего выражения в блоке теперь дропаются в конце блока, а не продолжают жить после него. Без этого изменения некоторый вполне нормально выглядящий код не компилировался:
// не компилируется до Rust 2024, компилируется после
fn f() -> usize {
let c = RefCell::new("..");
c.borrow().len()
}
🔸запрещены некоторые паттерны, которые крайне неочевидным образом взаимодействуют с pattern match ergonomics. Посмотрите по ссылке, там действительно странные вещи.
🔸
std::env::set_var
, std::env::remove_var
и std::os::unix::process::CommandExt::before_exec
теперь являются unsafe функциями. Сделать их unsafe
во всех редакциях, увы, мешает обратная совместимость.🔸cargo теперь по умолчанию учитывает MSRV при выборе версии зависимости.
Пачка изменений, связанных с unsafe кодом:
🔸
extern
-блоки теперь должны быть объявлены с unsafe, чтобы показать, что ответственность за корректность определений лежит на том, кто пишет extern
блок. Дополнительно определения внутри extern
-блока теперь могут быть помечены safe
. В этом случае компилятор будет предполагать, что для доступа и использования этих определений не нужны специальные предусловия, и их можно использовать вне unsafe
-блоков. В качестве примера можно привести sqrt
из стандартной библиотеки C: не смотря на то, что это внешняя функция, её можно вызвать с любым значением, не вызывая UB.🔸атрибуты
no_mangle
, export_name
и link_section
теперь должны быть записаны через #[unsafe(...)]. С этими атрибутами можно сломать гарантии, которые даёт компилятор, и компилятор не может проверить, что их использование корректно.🔸unsafe_op_in_unsafe_fn warn по умолчанию.
🔸ссылки на
static mut
являются по умолчанию ошибкой компиляции.🔸если компилятор не может вывести тип, к которому нужно привести выражение типа
!
, то он использует т. н. fallback. До Rust 2024 это был ()
, теперь это !
. Так как это может сломать существующий unsafe
-код, компилятор теперь по умолчанию даёт ошибку компиляции, если такое приведение происходит в unsafe-коде.▪️Теперь замыкания могут быть
async
!let mut vec: Vec<String> = vec![];
let closure = async || {
vec.push(ready(String::from("")).await);
};
В отличие от замыканий, возвращающих
async
-блок, футуры, возвращаемые этими замыканиями, могут использовать ссылки на то, что замыкание захватывает. В типах это выражается тем, что возвращаемые async
-замыканиями футуры связаны временем жизни с самим замыканием. Из-за того, что эту связь пока что невозможно выразить с обычными Fn*
-трейтами, в core::ops
добавили AsyncFnOnce, AsyncFnMut и AsyncFn. Как и обычные Fn*
-трейты, в ограничениях их можно использовать лишь в засахаренном виде.▪️
FromIterator
и Extend
теперь реализованы для всех кортежей до арности 12 включительно и имеют семантику, аналогичную Iterator::unzip
.▪️Для беззнаковых чисел, соответствующих
NonZero
и чисел с плавающей точкой добавили метод midpoint. Тривиальная вещь, но из-за переполнения её очень легко реализовать неправильно.▪️В
const
-контексте теперь можно использовать, помимо прочего, несколько простых арифметических функций на числах с плавающей точкой и swap
, как на ссылках, так и на сырых указателях.▪️
macro_rules
, объявленные макросами, теперь используют редакцию кода, в котором они раскрываются, вместо редакции кода, в котором определён внешний макрос.🎉12❤🔥2👍1
#prog #rust #rustreleasenotes
Вышла версия Rust 1.86.0! Как всегда, тут только примечательное для меня, а остальное — в полных заметках о релизе.
В этот раз релиз, по большому счёту, состоит из одной фичи (двух, если вы пишете код с SIMD).
▪️Как я уже писал, стабилизировали апкаст трейтов:
Этот апкаст также работает и через сырые указатели, поэтому пока что — до тех пор, пока не определились с точными гарантиями — создавать сырые указатели на
▪️По умолчанию компилятор Rust генерирует исполняемые файлы, которые могут работать на всех процессорах целевой платформы. Это означает, помимо всего прочего, невозможность использования процессорно-специфичных наборов инструкций (на практике важнее всего расширения для SIMD). Для того, чтобы обойти эти ограничения и заставить компилятор генерировать код с использованием дополнительного конкретного набора инструкций, в языке есть атрибут
Так как использование на процессоре инструкций, которые он не распознаёт — неопределённое поведение, вызов подобных функций должен быть только в unsafe блоке. Программист при вызове обещает, что этот код недостижим на платформах, на которых указанный набор инструкций не поддерживается.
Это логичное поведение, но у начальной реализации был заметный эргономический недостаток: вызов функции с, скажем,
▪️Продолжая тему, начатую в Rust 1.70.0, компилятор теперь при компиляции в отладочном профиле (и с флагом
Эта проверка уже нашла ошибки в реальных проектах, как во время crater run, так и после мерджа.
▪️Ошибка
▪️Теперь можно получить мутабельные ссылки по нескольким индексам/ключам из слайса и хэшмапы одновременно с новыми методами <[_]>::get_disjoint_mut и HashMap::get_disjoint_mut, вместе с unsafe *
Что мне не очень нравится в этих методах — неконсистентность. Именно, версия для слайсов возвращает массив ссылок или ошибку, причём как в случае пересекающихся индексов, так и в случае индексов, выходящих за длину слайса. Версия же для мапы возвращает массив опциональных ссылок (с
▪️
▪️В const-контексте теперь можно, помимо всего прочего, разбивать строку по индексу (включая мутабельно и с опциональным возвратом) и проверять, что индекс лежит между отдельными char-ами.
▪️Компилятор для Linux на ARM теперь компилируется с ThinLTO и PGO, что ускоряет его на бенчмарках на 10-20%, в некоторых случаях — даже на 30%!.
Вышла версия Rust 1.86.0! Как всегда, тут только примечательное для меня, а остальное — в полных заметках о релизе.
В этот раз релиз, по большому счёту, состоит из одной фичи (двух, если вы пишете код с SIMD).
▪️Как я уже писал, стабилизировали апкаст трейтов:
trait Super1 {}
trait Super2 {}
trait Combined: Super1 + Super2 {}
fn foo(x: &dyn Combined) {
let _: &dyn Super1 = x;
let _: &dyn Super2 = x;
}
Этот апкаст также работает и через сырые указатели, поэтому пока что — до тех пор, пока не определились с точными гарантиями — создавать сырые указатели на
dyn Trait
с невалидной vtable-частью запрещено и считается UB. В miri на этот счёт уже есть соответствующие проверки.▪️По умолчанию компилятор Rust генерирует исполняемые файлы, которые могут работать на всех процессорах целевой платформы. Это означает, помимо всего прочего, невозможность использования процессорно-специфичных наборов инструкций (на практике важнее всего расширения для SIMD). Для того, чтобы обойти эти ограничения и заставить компилятор генерировать код с использованием дополнительного конкретного набора инструкций, в языке есть атрибут
#[target_feature(enable = "...")]
. Он позволяет компилятору генерировать код с использованием расширенного набора инструкций. Так как использование на процессоре инструкций, которые он не распознаёт — неопределённое поведение, вызов подобных функций должен быть только в unsafe блоке. Программист при вызове обещает, что этот код недостижим на платформах, на которых указанный набор инструкций не поддерживается.
Это логичное поведение, но у начальной реализации был заметный эргономический недостаток: вызов функции с, скажем,
#[target_feature(enable = "avx")]
из другой функции с #[target_feature(enable = "avx")]
также требовал unsafe-блока, даже не смотря на то, что требование на корректный вызов в этом случается удовлетворяется автоматически и это можно проверить статически. Начиная с этой версии подобные функции можно вызывать друг из друга без unsafe-блока при условии, что наборы включённых фич совпадают.▪️Продолжая тему, начатую в Rust 1.70.0, компилятор теперь при компиляции в отладочном профиле (и с флагом
-C debug-assertions
) вставляет паникующие проверки на null при разыменовывании сырых указателей. К сожалению, так как std поставляется только в release-сборке, на коде из std это не сказывается. (Это можно обойти, но требует nightly).Эта проверка уже нашла ошибки в реальных проектах, как во время crater run, так и после мерджа.
▪️Ошибка
FromBytesWithNulError
, которая может быть возвращена из CStr::from_bytes_with_nul
, теперь является enum
и позволяет в случае nul внутри данных получить его позицию.▪️Теперь можно получить мутабельные ссылки по нескольким индексам/ключам из слайса и хэшмапы одновременно с новыми методами <[_]>::get_disjoint_mut и HashMap::get_disjoint_mut, вместе с unsafe *
_unchecked
вариантами, дающими UB при пересечении. Версия для слайсов при этом также позволяет индексировать диапазонами, а не только единичными индексами.Что мне не очень нравится в этих методах — неконсистентность. Именно, версия для слайсов возвращает массив ссылок или ошибку, причём как в случае пересекающихся индексов, так и в случае индексов, выходящих за длину слайса. Версия же для мапы возвращает массив опциональных ссылок (с
None
для ключей без значений) и паникует на пересекающихся ключах.▪️
Once
и OnceLock
обзавелись методами для блокировки в ожидании инициализации.▪️В const-контексте теперь можно, помимо всего прочего, разбивать строку по индексу (включая мутабельно и с опциональным возвратом) и проверять, что индекс лежит между отдельными char-ами.
▪️Компилятор для Linux на ARM теперь компилируется с ThinLTO и PGO, что ускоряет его на бенчмарках на 10-20%, в некоторых случаях — даже на 30%!.
👍9❤3🔥1
#prog #rust #rustreleasenotes
Вышла версия Rust 1.87.0! Как всегда, тут только выдержки, всё остальное — в полных заметках о релизе.
▪️Не фича, но: прошло ровно 10 лет с момента релиза Rust 1.0 🎉
▪️В качестве продолжения темы из предыдущего релиза очень много интринсиков из
▪️Точное указывание захватываемых обобщённых параметров (
▪️При написании impl-а трейта на
▪️Однажды в компиляторе был баг, из-за которого
▪️Унарные операторы теперь корректно парсятся при применении к диапазонам с открытой нижней границей. Как следствие, эта синтаксическая конструкция теперь корректно матчится с макро-фрагментом expr в декларативных макросах, что может привести к тому, что некоторые макросы теперь могут раскрываться иначе.
▪️В стандартную библиотеку добавили поддержку каналов (в смысле pipes). Из неприятного: так как это тонкая обёртка над примитивами OS, указать ёмкость этих каналов нельзя.
▪️Для примитивных чисел добавили методы unbounded_shl и unbounded_shr. Обычные битовые сдвиги сдвигают числа не на указанное число бит, а на число бит, равное остатку от деления указанного числа на битовую ширину. Это хорошо ложится на команды процессора, но это — крайне неполезная для программиста семантика.
▪️У знаковых чисел добавили метод cast_unsigned, а у беззнаковых — cast_signed (включая
▪️Ещё беззнаковым числам добавили метод is_multiple_of. Казалось бы, зачем?
▪️Из несколько более значимого: стабилизировали
▪️Также коллекцию методов
▪️
▪️Целую пачку API стабилизировали для использования в const-контекстах. Наиболее значимым, пожалуй, является
▪️Вывод
▪️Парочка изменений, связанных с fmt-макросами. Во-первых, в одной из версий из-за изменений внутреннего представления стало возможным использовать
Вышла версия Rust 1.87.0! Как всегда, тут только выдержки, всё остальное — в полных заметках о релизе.
▪️Не фича, но: прошло ровно 10 лет с момента релиза Rust 1.0 🎉
▪️В качестве продолжения темы из предыдущего релиза очень много интринсиков из
std::arch
теперь можно вызывать без unsafe-блоков в функциях, на которых есть #[target_feature]
, включающая набор инструкций с этими интринсиками.▪️Точное указывание захватываемых обобщённых параметров (
use<...>
) теперь работает и для impl Trait
в методах трейтов. К сожалению, опускать обобщённые типовые и const-овые параметры всё ещё нельзя.▪️При написании impl-а трейта на
dyn Trait
теперь необязательно реализовывать методы с Self: Sized, так как их всё равно нельзя вызвать.▪️Однажды в компиляторе был баг, из-за которого
dyn A + B
и dyn B + A
считались разными типами и, как следствие, для них можно было написать две разные реализации одного трейта. Баг поправили, но, так как это номинально слом обратной совместимости, оставили это предупреждением. С версии 1.87.0 это теперь ошибка компиляции.▪️Унарные операторы теперь корректно парсятся при применении к диапазонам с открытой нижней границей. Как следствие, эта синтаксическая конструкция теперь корректно матчится с макро-фрагментом expr в декларативных макросах, что может привести к тому, что некоторые макросы теперь могут раскрываться иначе.
▪️В стандартную библиотеку добавили поддержку каналов (в смысле pipes). Из неприятного: так как это тонкая обёртка над примитивами OS, указать ёмкость этих каналов нельзя.
▪️Для примитивных чисел добавили методы unbounded_shl и unbounded_shr. Обычные битовые сдвиги сдвигают числа не на указанное число бит, а на число бит, равное остатку от деления указанного числа на битовую ширину. Это хорошо ложится на команды процессора, но это — крайне неполезная для программиста семантика.
unbounded_*
методы лишены этой проблемы: при сдвиге на значение, большее битовой ширины, методы возвращают 0 для сдвига влево, 0 для сдвига вправо у беззнаковых чисел и размноженный знаковый бит для знаковых чисел (то есть 0 для положительных и -1 для отрицательных).▪️У знаковых чисел добавили метод cast_unsigned, а у беззнаковых — cast_signed (включая
NonZero
). Да, это полностью аналогично использованию кастов через as
, но эти методы позволяют убедиться, что битовая ширина не меняется.▪️Ещё беззнаковым числам добавили метод is_multiple_of. Казалось бы, зачем?
This function is equivalent to self % rhs == 0, except that it will not panic for rhs == 0. Instead, 0.is_multiple_of(0) == true, and for any non-zero n, n.is_multiple_of(0) == false.
▪️Из несколько более значимого: стабилизировали
extract_if
для вектора и для связного списка. Оба итератора извлекают из коллекции элементы, соответствующие предикату, и оба позволяют модифицировать переданные элементы. Версия для вектора ещё и позволяет указать диапазон, на котором применяется фильтрация.▪️Также коллекцию методов
take_*
на слайсах для извлечения указанных частей (со странными на первый взгляд типами self
: &mut &{,mut}[T]
) стабилизировали с именами split_off_*▪️
OsStr{, ing}
теперь имеют методы display, которые возвращают реализующую Display
обёртку. По очевидным причинам обёртка может при выводе терять данные из-за невозможности представить некоторые элементы в UTF-8.▪️Целую пачку API стабилизировали для использования в const-контекстах. Наиболее значимым, пожалуй, является
<[T]>::copy_from_slice
.▪️Вывод
Debug
для сырых указателей теперь включает не только адрес, но и метаданные, при наличии.▪️Парочка изменений, связанных с fmt-макросами. Во-первых, в одной из версий из-за изменений внутреннего представления стало возможным использовать
format_args!
(в том числе не напрямую, а через panic!
) вида format_args!("{}", "a")
в const-контекстах. Эта было ненамеренно, поэтому в 1.87.0 эту возможность прикрыли. Во-вторых, значения для указания максимальной ширины форматирования и максимальной точности теперь ограничены 16 битами. Звучит смешно, но некоторые либы это сломало.👍11🥰3🥴1
#prog #rust #rustreleasenotes
Вышла версия Rust 1.88.0! Как всегда, тут только кусочки, всё остальное — в детальных заметках о релизе.
▪️Наконец-то стабилизировали let chains! 🎉🎉🎉 Джва года ждал. Пример утащу прям из блогопоста:
К сожалению, из-за соображений обратной совместимости, связанных с временем жизни временных выражений в
▪️Специфическая вещь: стабилизировали так называемые naked functions — функции, для которых не генерирует при кодгене пролог и эпилог. Их тело состоит из ассемблерного кода. Периодически требуется для написания низкоуровневого кода.
▪️В cfg-атрибутах теперь можно использовать литералы true и false. Как пишут в блогопосте:
▪️В Rust вызов методов автоматически создаёт ссылку, если метод вызывается на значении. Это удобно, но иногда позволяет написать код, который делает потенциально небезопасную операцию и при этом не выглядит таковым. Именно, если значение, на котором вызывается метод, является результатом разыменовывания сырого указателя, то для корректности этого вызова нужно, чтобы указатель не был null, указывал на корректное значение и был корректно выровнен — иными словами, чтобы указатель удовлетворял требованиям корректности для ссылки. Для того, чтобы отлавливать подобные ошибки, добавили для этого линт. Пока что он warn by default, но в будущем планируют поднять до deny by default.
▪️Для
▪️Стабилизировали некоторые API:
🔸Cell::update — наверное, один из самых частых паттернов использования
🔸
🔸
🔸Целая пачка геттеров для proc_macro::Span. Позволит писателям процедурных макросов делать более полезные диагностики.
🔸<[T]>::as_chunks, который разбивает переданный слайс на слайс массивов переданного const-параметром длины и остаток, который не влез, вместе с мутабельными вариациями, unchecked (UB, если остаток не пустой и длина нужных массивов равна нулю) и разбитием в обратную сторону.
🔸std::hint::select_unpredictable. Вызов
Дополнительно некоторые API теперь доступны в const контекстах:
🔸NonNull::replace и <*mut T>::replace
🔸std::ptr::swap_nonoverlapping
🔸Пачка методов на
▪️Cargo теперь умеет автоматически удалять старые файлы из кеша. Именно, на тяжёлых командах проверяет и удаляет скаченные файлы старше трёх месяцев и файлы из локальной файловой системы старше месяца. К сожалению, пороги не настраиваются.
▪️Cargo теперь использует zlib-rs для операций, требующих (за, рас)паковки gzip-файлов (например, cargo package). Для пользователя ничего не поменялось, но соответствующие операции стали работать быстрее. В pull request-е приводится в пример windows-bindgen, для которого cargo package работает на 60% (!) быстрее.
▪️Для rustdoc стабилизировали флаги, позволяющие указывать внешние программы, используемые для запуска док-тестов, требующих кросс-компиляции. Также
Вышла версия Rust 1.88.0! Как всегда, тут только кусочки, всё остальное — в детальных заметках о релизе.
▪️Наконец-то стабилизировали let chains! 🎉🎉🎉 Джва года ждал. Пример утащу прям из блогопоста:
if let Channel::Stable(v) = release_info()
&& let Semver { major, minor, .. } = v
&& major == 1
&& minor == 88
{
println!("`let_chains` was stabilized in this version");
}
К сожалению, из-за соображений обратной совместимости, связанных с временем жизни временных выражений в
if let
, эта фича доступна только в edition 2024.▪️Специфическая вещь: стабилизировали так называемые naked functions — функции, для которых не генерирует при кодгене пролог и эпилог. Их тело состоит из ассемблерного кода. Периодически требуется для написания низкоуровневого кода.
▪️В cfg-атрибутах теперь можно использовать литералы true и false. Как пишут в блогопосте:
Previously, empty predicate lists could be used for unconditional configuration, like cfg(all()) for enabled and cfg(any()) for disabled, but this meaning is rather implicit and easy to get backwards. cfg(true) and cfg(false) offer a more direct way to say what you mean.
▪️В Rust вызов методов автоматически создаёт ссылку, если метод вызывается на значении. Это удобно, но иногда позволяет написать код, который делает потенциально небезопасную операцию и при этом не выглядит таковым. Именно, если значение, на котором вызывается метод, является результатом разыменовывания сырого указателя, то для корректности этого вызова нужно, чтобы указатель не был null, указывал на корректное значение и был корректно выровнен — иными словами, чтобы указатель удовлетворял требованиям корректности для ссылки. Для того, чтобы отлавливать подобные ошибки, добавили для этого линт. Пока что он warn by default, но в будущем планируют поднять до deny by default.
▪️Для
array::from_fn
добавили гарантию, что переданная функция вызывается по порядку индексов. Актуально, если вызывается с некоторым замыканием, которое захватывает и мутирует стейт.▪️Стабилизировали некоторые API:
🔸Cell::update — наверное, один из самых частых паттернов использования
Cell
.🔸
extract_if
для HashMap и для HashSet.🔸
Default
для сырых указателей (ожидаемо, null).🔸Целая пачка геттеров для proc_macro::Span. Позволит писателям процедурных макросов делать более полезные диагностики.
🔸<[T]>::as_chunks, который разбивает переданный слайс на слайс массивов переданного const-параметром длины и остаток, который не влез, вместе с мутабельными вариациями, unchecked (UB, если остаток не пустой и длина нужных массивов равна нулю) и разбитием в обратную сторону.
🔸std::hint::select_unpredictable. Вызов
select_unpredictable(cond, true_val, false_val)
аналогичен if cond { true_val } else { false_val }
, но сигнализирует оптимизатору, что процессор навряд ли сможет корректно предсказать условие (читай, просит использовать cmov-подобные операции вместо явных ветвлений). Используется, например, в реализации двоичного поиска в std.Дополнительно некоторые API теперь доступны в const контекстах:
🔸NonNull::replace и <*mut T>::replace
🔸std::ptr::swap_nonoverlapping
🔸Пачка методов на
Cell
: replace
, get
, get_mut
, from_mut
и as_slice_of_cells
.▪️Cargo теперь умеет автоматически удалять старые файлы из кеша. Именно, на тяжёлых командах проверяет и удаляет скаченные файлы старше трёх месяцев и файлы из локальной файловой системы старше месяца. К сожалению, пороги не настраиваются.
▪️Cargo теперь использует zlib-rs для операций, требующих (за, рас)паковки gzip-файлов (например, cargo package). Для пользователя ничего не поменялось, но соответствующие операции стали работать быстрее. В pull request-е приводится в пример windows-bindgen, для которого cargo package работает на 60% (!) быстрее.
▪️Для rustdoc стабилизировали флаги, позволяющие указывать внешние программы, используемые для запуска док-тестов, требующих кросс-компиляции. Также
ignore-*
-атрибуты на док-тестах позволяют указывать цели компиляции, на которых док-тест не должен компилироваться.👍14❤7🤡2🤮1😐1
#prog #rust #rustreleasenotes
Вышла версия Rust 1.89.0! Как всегда, тут только избранные части, остальное — в детальных заметках о релизе.
▪️Стабилизировали вывод const generics в выражениях!
К сожалению, использовать
▪️Док-тесты теперь запускаются при кросс-компиляции.
▪️Атрибуты вида
▪️После фиксов для согласования с существующими компиляторами C/C++ теперь можно использовать
▪️Как я уже писал, продление времени жизни временных значений теперь работает и с кортежными конструкторами (кортежных структур и кортежных вариантов
▪️Стабилизированы интринсики (и связанные с ними target feature [1], [2]) из наборов инструкций AVX512, SHA512, SM3 и SM4 для x86-64.
▪️Макро-фрагмент в декларативных макросах без указанного типа теперь является ошибкой компиляции на всех edition.
▪️Результат вычисления format_args! теперь можно сохранять в переменных!🎉
▪️
▪️
▪️К слову о локах, для
▪️
Вышла версия Rust 1.89.0! Как всегда, тут только избранные части, остальное — в детальных заметках о релизе.
▪️Стабилизировали вывод const generics в выражениях!
pub fn all_false<const LEN: usize>() -> [bool; LEN] {
[false; _]
}
К сожалению, использовать
_
для const generics в сигнатурах всё ещё нельзя.▪️Док-тесты теперь запускаются при кросс-компиляции.
▪️Атрибуты вида
#![doc(test(attr(..)))]
(которые добавляют #[attr(..)] ко всем док-тестам в модуле) теперь можно использовать везде, включая корень крейта.▪️После фиксов для согласования с существующими компиляторами C/C++ теперь можно использовать
i128
и u128
в extern "C"
определениях, интероп корректно работает и, как следствие, improper_ctypes_definitions
более не триггерится.▪️Как я уже писал, продление времени жизни временных значений теперь работает и с кортежными конструкторами (кортежных структур и кортежных вариантов
enum
-ов).▪️Стабилизированы интринсики (и связанные с ними target feature [1], [2]) из наборов инструкций AVX512, SHA512, SM3 и SM4 для x86-64.
▪️Макро-фрагмент в декларативных макросах без указанного типа теперь является ошибкой компиляции на всех edition.
▪️Результат вычисления format_args! теперь можно сохранять в переменных!
▪️
std::array::IntoIter
теперь реализовывает Default. Значение по умолчанию при этом является итератором, который ничего не возвращает. Полезно, когда нужно предоставить итерацию по опциональному массиву и при этом не хочется прокидывать Option
в возвращаемое значение.▪️
LazyCell
и LazyLock
теперь реализуют DerefMut. Одной причиной использовать once_cell
меньше.▪️К слову о локах, для
File
теперь есть пачка методов ({, try_}lock{, _shared}
и unlock
) для работы с платформо-специфичными способами блокировки файлов. Напоминаю, что на Linux эти функции требуют кооперации: коду нужно явно указывать на то, что он обращает внимание на блокировки, а по умолчанию они ничего для сторонних процессов не делают.▪️
NonNull
можно безопасно создать из ссылки, поскольку ссылки в Rust обязаны не быть null. Для этого есть реализация From<&T> for NonNull<T>
. К сожалению, этот способ конвертации не очень очевиден, и на практике многие писали код с NonNull::new_unchecked
, в котором ссылка приводилась к сырому указателю по месту вызова. Видимо, чтобы сделать безопасный способ конвертации более видимым и очевидным в исходниках, к NonNull
добавили конструктор from_ref (и from_mut для конвертации из &mut T
). Также этому типу добавили методы для работы с provenance (expose_provenance, with_exposed_provenance, without_provenance), которые аналогичны таковым для сырых указателей, но принимают/возвращают NonZero<usize>
вместо usize
.Please open Telegram to view this post
VIEW IN TELEGRAM
👍3🔥3❤2