Блог*
I love Rust for letting me write self-describing code trait Т { type T; const TT: (Self::T, Self::T); } trait ТТ<T> { const Т: T; } trait Sеlf { const Sеlf: Self; } const fn t<T: Т<T = isize>>() -> isize { impl<T: ТТ<T>> Sеlf for T…
#prog #rust #моё
Что ж, наверное, стоит объяснить, а что это вообще за код такой.
Странно выглядят фрагменты вроде
Если переписать код, чтобы использовать лишь ASCII имена (и заодно привести его под принятые соглашения о наименованиях), то получится следующее:
Если вынести на верхний уровень impl-блоки (как я бы и сделал в реальном коде) и немного переупорядочить для ясности, то получится вот это:
ответ к загадке этого кода строчку, которая фактически является ассертом времени компиляции на то, что численное значение
Что ж, наверное, стоит объяснить, а что это вообще за код такой.
Странно выглядят фрагменты вроде
trait Sеlf {
const Sеlf: Self;
}
иconst fn t<T: Т<T = isize>>
, поскольку они вроде бы не должны компилироваться из-за совпадающих имён. Дело в том, что я использовал в Sеlf
кириллическую е
(и в имени трейта, и в имени ассоциированного типа), а в именах трейтов Т
и ТТ
— соответственно кириллическую Т
. Это разрешено с версии Rust 1.53. При этом компилятор выдаёт (помимо всего прочего) вот такое предупреждение на этот код:warning: the usage of Script Group `Cyrillic` in this crate consists solely of mixed script confusables
--> src/lib.rs:1:7
|
1 | trait Т {
| ^
|
= note: the usage includes 'Т' (U+0422), 'е' (U+0435)
= note: please recheck to make sure their usages are indeed what you want
= note: `#[warn(mixed_script_confusables)]` on by default
И также указывает на пары идентификаторов, которые могут быть перепутаны. Полезный компилятор, как ни крути.Если переписать код, чтобы использовать лишь ASCII имена (и заодно привести его под принятые соглашения о наименованиях), то получится следующее:
trait PairValue {
type T;
const PAIR_VALUE: (Self::T, Self::T);
}
trait GenericValue<T> {
const GENERIC_VALUE: T;
}
trait SelfValue {
const SELF_VALUE: Self;
}
const fn t<T: PairValue<T = isize>>() -> isize {
impl<T: GenericValue<T>> SelfValue for T {
const SELF_VALUE: Self = T::GENERIC_VALUE;
}
T::PAIR_VALUE.0 * T::PAIR_VALUE.1 + 20
}
const fn self_describing<T: SelfValue>() -> T {
impl<T: PairValue<T = isize>> GenericValue<isize> for T {
const GENERIC_VALUE: isize = t::<T>();
}
T::SELF_VALUE
}
enum The {
Answer = {
impl PairValue for isize {
type T = isize;
const PAIR_VALUE: (isize, isize) = (2, 11);
}
self_describing()
},
}
const _ASSERT: [(); 42] = [(); The::Answer as usize];
Вроде выглядит немного получше, но всё ещё запутанно. В Rust, как известно, (почти) всё является выражением. Различного рода top level определения (называемые item в reference) также могут быть использованы, как выражения, и при этом возвращают ()
. Очень много куда можно воткнуть impl
-блок — в частности, внутри функции и внутри выражения. В том числе внутри выражения для дискриминанта варианта enum
. Разумеется, при этом применяются обычные правила видимости: нельзя реализовать трейт, если трейт или тип, для которого они определяются, не видны в текущей области видимости, но сами impl-ы трейтов видны всюду, где виден сам трейт. Тут эти правила видимости, впрочем, не мешают.Если вынести на верхний уровень impl-блоки (как я бы и сделал в реальном коде) и немного переупорядочить для ясности, то получится вот это:
trait PairValue {
type T;
const PAIR_VALUE: (Self::T, Self::T);
}
impl PairValue for isize {
type T = isize;
const PAIR_VALUE: (isize, isize) = (2, 11);
}
trait GenericValue<T> {
const GENERIC_VALUE: T;
}
const fn t<T: PairValue<T = isize>>() -> isize {
T::PAIR_VALUE.0 * T::PAIR_VALUE.1 + 20
}
impl<T: PairValue<T = isize>> GenericValue<isize> for T {
const GENERIC_VALUE: isize = t::<T>();
}
trait SelfValue {
const SELF_VALUE: Self;
}
impl<T: GenericValue<T>> SelfValue for T {
const SELF_VALUE: Self = T::GENERIC_VALUE;
}
const fn self_describing<T: SelfValue>() -> T {
T::SELF_VALUE
}
enum The {
Answer = self_describing(),
}
const _ASSERT: [(); 42] = [(); The::Answer as usize];
В последней строчке мы находим The::Answer
является 42. Откуда берётся это значение? Выше Answer
присваивается значение вызова определённой парой строк выше функции self_describing
(кстати, использовать трейты на обобщённых параметрах в const fn стало возможным с версии 1.61). Но это обобщённая функция — так какой же тип там выводится?Telegram
Блог*
#prog #rust #rustreleasenotes
Да, да, я знаю, что я ленивая жягодица, не напоминайте.
Вышел Rust 1.53.0! Как обычно, я не буду разбирать релиз целиком, а выделю лишь то, что привлекло лично моё внимание.
Но начну я, пожалуй, с парочки вещей, которые мне…
Да, да, я знаю, что я ленивая жягодица, не напоминайте.
Вышел Rust 1.53.0! Как обычно, я не буду разбирать релиз целиком, а выделю лишь то, что привлекло лично моё внимание.
Но начну я, пожалуй, с парочки вещей, которые мне…
❤1😐1
Ну а теперь серьёзно.
#prog #rust #rustreleasenotes
Вышла версия Rust 1.71.0! Как обычно, тут только избранные куски, а все изменения в ченджлоге.
▪️Новый вариант опции атрибута
▪️ Уже имеющиеся варианты ABI обзавелись
Это изменение сделает более удобным разработку проектов, смешивающих код на Rust с кодом на других языках. Не
▪️Ещё одно изменение, связанное с паниками, но уже исключительно в рамках Rust. Ранее паника во время паники приводила к немедленному аборту процесса. Теперь это ограничение несколько ослабили: паника во время паники не приводит к аборту при условии, что она перехватывается на границе реализации
Но в одном аспекте требования к паникам, наоборот, сделали строже: паника внутри хука на панику сразу ведёт к аборту. В реальном коде это приводило к дедлоку из-за того, что рантайм держал блокировку на глобальный лок для бектрейса.
▪️Стало возможным включать в бинарники скрипты для визуализации значений пользовательских типов в отладчиках. Без этих скриптов в отладчике значение, скажем,
▪️Обновлена версия MUSL, используемая для *-linux-musl. Главным образом это означает, что 64-битная версия time_t, позволяющая избежать проблемы 2038 года, используется безусловно для всех систем, включая 32-битные.
▪️Макрос
* в этих случаях в результирующем коде теперь меньше виртуальных вызовов
* как следствие, макросы вроде log::info! теперь не вредят производительности из-за пробрасывания аргументов через
* core::fmt::Arguments::as_str() теперь чаще возвращает
* в бинаре теперь может сильно распухнуть секция
▪️Несколько изменений докатились до стейбла: улучшения расчёта раскладки типов, фикс расчёта приватности для Self-конструктора у кортежных структур, документация для const-инициализации thread local статиков, стабилизация BuildHasher::hash_one.
#prog #rust #rustreleasenotes
Вышла версия Rust 1.71.0! Как обычно, тут только избранные куски, а все изменения в ченджлоге.
▪️Новый вариант опции атрибута
#[link]
— конкретно kind="raw-dylib"
— позволяет на Windows линковаться с динамическими библиотеками, не требуя их наличия во время компиляции. Кросс-компиляция стала немного проще. А, и ещё атрибуты для импорта и экспорта символом из/в DLL, но я недостаточно в этом разбираюсь, чтобы понять разницу.▪️ Уже имеющиеся варианты ABI обзавелись
"*-unwind"
вариантами (полный список). Как нетрудно догадаться, паники и исключения, вызывающую раскрутку стека, могут проходить границу между языками. В частности, это означает, что исключения из C++ могут проходить через фреймы Rust-кода, а растовая паника может проходить через фреймы C++-кода. Ловить раскрутку стека, впрочем, можно лишь в фреймах того языка, в котором она была начата.Это изменение сделает более удобным разработку проектов, смешивающих код на Rust с кодом на других языках. Не
*-unwind
варианты ABI, как и раньше, абортят процесс, если раскрутка стека пытается пройти по FFI.▪️Ещё одно изменение, связанное с паниками, но уже исключительно в рамках Rust. Ранее паника во время паники приводила к немедленному аборту процесса. Теперь это ограничение несколько ослабили: паника во время паники не приводит к аборту при условии, что она перехватывается на границе реализации
drop
. Иными словами, паники внутри деструкторов — которые сами могут быть вызваны из-за паники — могут быть перехвачены и корректно обработаны, не приводя к немедленному прекращению работы.Но в одном аспекте требования к паникам, наоборот, сделали строже: паника внутри хука на панику сразу ведёт к аборту. В реальном коде это приводило к дедлоку из-за того, что рантайм держал блокировку на глобальный лок для бектрейса.
▪️Стало возможным включать в бинарники скрипты для визуализации значений пользовательских типов в отладчиках. Без этих скриптов в отладчике значение, скажем,
Vec<i32>
выглядело бы не как последовательность чисел, а как набор из адреса, длины и ёмкости — что, очевидно, не слишком-то полезно. Такими скриптами для визуализации укомплектовывается std. Новый механизм позволяет использовать этот же механизм и пользователям Rust.▪️Обновлена версия MUSL, используемая для *-linux-musl. Главным образом это означает, что 64-битная версия time_t, позволяющая избежать проблемы 2038 года, используется безусловно для всех систем, включая 32-битные.
▪️Макрос
format_args!
(на который опираются куча макросов как в std, так и вне её) теперь инлайнит форматируемые значения в форматные строки, если они являются литералами или вложенными format_args!. Если наглядно, то следующие пары вызовов теперь эквивалентны даже не на уровне бинарного кода, а ещё на уровне HIR:println!("Hello, {}!", "World");Из того, на что на это влияет:
println!("Hello, World!");
println!("[info] {}", format_args!("error"));
println!("[info] error");
println!("[{}] {}", status, format_args!("error: {}", msg));
println!("[{}] error: {}", status, msg);
println!("{} + {} = {}", 1, 2, 1 + 2);
println!("1 + 2 = {}", 1 + 2);
* в этих случаях в результирующем коде теперь меньше виртуальных вызовов
fmt
-методов;* как следствие, макросы вроде log::info! теперь не вредят производительности из-за пробрасывания аргументов через
format_args!
;* core::fmt::Arguments::as_str() теперь чаще возвращает
Some(_)
;* в бинаре теперь может сильно распухнуть секция
.text
из-за большего количества уникальных строк — особенно, если в коде активно используется макрос dbg!. Не то, чтобы это было большой проблемой, но может выйти боком, если проект компилируется, скажем, под embedded, где каждый байт на счету.▪️Несколько изменений докатились до стейбла: улучшения расчёта раскладки типов, фикс расчёта приватности для Self-конструктора у кортежных структур, документация для const-инициализации thread local статиков, стабилизация BuildHasher::hash_one.
blog.rust-lang.org
Announcing Rust 1.71.0 | Rust Blog
Empowering everyone to build reliable and efficient software.
👍8🎉2❤1👎1
#prog #rust #rustreleasenotes
Вышла версия Rust 1.72.0! Как обычно, тут только то, что интересует меня, а полный ченджлог тут.
▪️Офигенно полезная вещь: компилятор теперь говорит о том, что имена не определены из-за того, что определяющий их код находится под неактивным
▪️Компилятор теперь не имеет ограничения на время вычисления const fn (технически ограничено включённым по умолчанию линтом, но его можно и отключить). При этом он всё ещё выдаёт предупреждения, если код из-за этого долго компилируется, но интервал между ними удваивается после каждого вывода, чтобы не спамить в консоль.
▪️Несколько линтов из Clippy втащили в компилятор, а именно:
🔹undropped_manually_drops (warn по умолчанию) — попытка явно дропнуть
🔹invalid_utf8_in_unchecked — разделив при этом на две:
🔹cmp_nan как invalid_nan_comparisons (warn по умолчанию) — явное сравнение с NaN (такое сравнение всегда возвращает ложь)
🔹cast_ref_to_mut — при кастах из
▪️Для указания трейт-объектов теперь не нужно выписывать ассоциированные типы, на которых есть ограничение
▪️Отправляющая половинка mpsc-канала из std наконец-то реализует
▪️Уточнено поведение
▪️Как я уже говорил,
▪️Опять-таки, как я уже рассказывал,
▪️Стабилизирован
Вышла версия Rust 1.72.0! Как обычно, тут только то, что интересует меня, а полный ченджлог тут.
▪️Офигенно полезная вещь: компилятор теперь говорит о том, что имена не определены из-за того, что определяющий их код находится под неактивным
#[cfg]
.▪️Компилятор теперь не имеет ограничения на время вычисления const fn (технически ограничено включённым по умолчанию линтом, но его можно и отключить). При этом он всё ещё выдаёт предупреждения, если код из-за этого долго компилируется, но интервал между ними удваивается после каждого вывода, чтобы не спамить в консоль.
▪️Несколько линтов из Clippy втащили в компилятор, а именно:
🔹undropped_manually_drops (warn по умолчанию) — попытка явно дропнуть
ManuallyDrop
.🔹invalid_utf8_in_unchecked — разделив при этом на две:
🔸invalid_utf8_in_unchecked
(deny по умолчанию) при вызове std::str::from_utf8_unchecked{, _mut}
на невалидном UTF-8 — это всегда неопределённое поведение🔸invalid_from_utf8
(warn по умолчанию) при вызове std::str::from_utf8{, _mut}
на невалидном UTF-8 — такой вызов всегда возвращает ошибку🔹cmp_nan как invalid_nan_comparisons (warn по умолчанию) — явное сравнение с NaN (такое сравнение всегда возвращает ложь)
🔹cast_ref_to_mut — при кастах из
&T
в &mut T
. allow по умолчанию, но исключительно из-за наличия false positive, к следующему релизу планируют сделать уже deny по умолчанию.▪️Для указания трейт-объектов теперь не нужно выписывать ассоциированные типы, на которых есть ограничение
Self: Sized
. Это консистентно с where Self: Sized
на методах, наличие которых не влияет на object safety, но которые нельзя вызвать на трейт-объектах. Отмечу, что указывать остальные типы для трейт-объектов всё также надо.▪️Отправляющая половинка mpsc-канала из std наконец-то реализует
Sync
.▪️Уточнено поведение
HashSet::insert
: если ключ уже есть в множестве, то он не заменяется, а переданный ключ дропается.▪️Как я уже говорил,
select_nth_unstable
теперь имеет реальную задокументированную линейную сложность.▪️Опять-таки, как я уже рассказывал,
ptr_eq
на счётчиках ссылок теперь сравнивает лишь адреса.▪️Стабилизирован
impl TryFrom<&OsStr> for &str
▪️В const-контексте теперь можно использовать CStr::from_bytes_with_nul, CStr::to_bytes, CStr::to_bytes_with_nul и CStr::to_str.👍8🔥2
#prog #rust #rustreleasenotes
Вышла версия Rust 1.73.0! Как обычно, тут только выдержки, а полный ченджлог — для компилятора, для cargo и для clippy.
В целом довольно минорный релиз, сильных причин обновлять нету.
▪️Текущее поведение компилятора — считать
▪️В макросах теперь можно вставлять метапеременные типа block после ключевых слов
▪️Как и обещано, линт
▪️Задокументирована текущая (v0) используемая rustc версия манглинга имён.
▪️Строку теперь можно индексировать парами
Код:
▪️Для
▪️Для примитивных беззнаковых числовых типов доступны методы div_ceil (деление с округлением вверх, наконец-то!), next_multiple_of и checked_next_multiple_of. Все из них работают в cosnt-контексте.
▪️Ещё в const-контексте теперь можно создавать слабые ссылки (и Arc-сорта тоже) и переводить NonNull в ссылку.
Вышла версия Rust 1.73.0! Как обычно, тут только выдержки, а полный ченджлог — для компилятора, для cargo и для clippy.
В целом довольно минорный релиз, сильных причин обновлять нету.
▪️Текущее поведение компилятора — считать
impl
-ы трейтов неперекрывающимися, если попытка их унифицировать приводит к циклу в логике. Теперь на это поведение выдаётся предупреждение, поскольку, возможно, это могут поменять в будущем.▪️В макросах теперь можно вставлять метапеременные типа block после ключевых слов
try
и async
. Пример кода, который не работал раньше, но работает теперь (результат раскрытия второго макроса, конечно, всё ещё требует активации фичи):▪️Как я уже писал, компилятор теперь ловит безусловную рекурсию в дропах.
macro_rules! create_async {
($body:block) => {
async $body
};
}
macro_rules! create_try {
($body:block) => {
try $body
};
}
▪️Как и обещано, линт
cast_ref_to_mut
(на касты из &T
в &mut T
— в том числе и не напрямую) теперь deny
по умолчанию.▪️Задокументирована текущая (v0) используемая rustc версия манглинга имён.
▪️Строку теперь можно индексировать парами
core::ops::Bound
▪️Немного поменяли формат сообщений паник по умолчанию для assert!
и assert_eq!
/assert_ne!
. Примеры:Код:
До:
fn main() {
let file = "ferris.txt";
panic!("oh no! {file:?} not found!");
}
После:
thread 'main' panicked at 'oh no! "ferris.txt" not found!', src/main.rs:3:5
Код:
thread 'main' panicked at src/main.rs:3:5:
oh no! "ferris.txt" not found!
До:
fn main() {
assert_eq!("🦀", "🐟", "ferris is not a fish");
}
После:
thread 'main' panicked at 'assertion failed: `(left == right)`
left: `"🦀"`,
right: `"🐟"`: ferris is not a fish', src/main.rs:2:5
По моему, стало более читаемо.
thread 'main' panicked at src/main.rs:2:5:
assertion `left == right` failed: ferris is not a fish
left: "🦀"
right: "🐟"
▪️Для
LocalKey<Cell<T>>
и LocalKey<RefCell<T>>
(LocalKey
— тип, в который заворачиваются значения в макросе thread_local!) добавили несколько методов для прямой манипуляции с значениями, без использования общего with
. Мало того, что это позволяет сделать код нагляднее, так ещё и позволяет в некоторых случаях избежать инициализации thread local переменной значением, которое будет тут же перезаписано. При этом в общности API не теряет, поскольку на практике почти всегда из-за требований внутренней изменяемости значение и так было завёрнуто в Cell
или RefCell
.▪️Для примитивных беззнаковых числовых типов доступны методы div_ceil (деление с округлением вверх, наконец-то!), next_multiple_of и checked_next_multiple_of. Все из них работают в cosnt-контексте.
▪️Ещё в const-контексте теперь можно создавать слабые ссылки (и Arc-сорта тоже) и переводить NonNull в ссылку.
GitHub
rust/RELEASES.md at master · rust-lang/rust
Empowering everyone to build reliable and efficient software. - rust-lang/rust
👍9❤1
#prog #rust #rustreleasenotes
Вышла версия Rust 1.74.0! В этот раз довольно минорный релиз, изменения в основном в тулинге. Как всегда, полный ченджлог отдельно, а тут лишь выдержки.
▪️Компилятор теперь позволяет использовать в непрозрачных возвращаемых типах проекции из
▪️Ранее замыкания, которые захватывали по ссылке поля
▪️Насчёт
▪️Насчёт линтов: задавать их теперь можно через секцию в манифесте Cargo.toml. С учётом того, что эта секция наследуется в workspace, это позволяет убедиться, что в группе связанных проектов используется идентичный набор глобальных линтов, без необходимости синхронизировать их руками.
▪️
▪️Стабилизировали пачку API в стандартной библиотеке, в том числе:
🔸core::num::Saturating — адаптер для примитивных числовых типов, реализующий насыщающую семантику для арифметических операций:
🔸Реализации
Дополнительно следующие API теперь могут быть использованы в константном контексте:
🔸
▪️Как я уже писал,
▪️rustdoc теперь позволяет добавлять свои CSS-классы к блокам кода и отдельные блоки для предупреждений.
▪️В сгенерированной rustdoc документации теперь можно искать с использованием типовых параметров.
Для примера, это означает, что
Вышла версия Rust 1.74.0! В этот раз довольно минорный релиз, изменения в основном в тулинге. Как всегда, полный ченджлог отдельно, а тут лишь выдержки.
▪️Компилятор теперь позволяет использовать в непрозрачных возвращаемых типах проекции из
Self
, в которые входят лайфтаймы не из сигнатуры функции. На практике это означает, что функции с impl Trait
и async
-функции, в возвращаемом типе которых есть Self
, теперь работают всегда, а не наталкиваются на произвольные ограничения компилятора. Подробнее вместе с примерами кода, который не компилировался раньше и стал приниматься сейчас, смотри в соответствующем PR.▪️Ранее замыкания, которые захватывали по ссылке поля
#[repr(packed)]
структур, захватывали их по разному в зависимости от того, являлась ли поле корректно выровненным или нет. Из-за этого смена типа поля в packed структуре — даже не того, что было захвачено! — могло привести к смене раскладке замыкания и, как следствие, изменению поведения из-за смены порядка дропа полей. В этой версии компилятора решили избавиться от столь странного поведения: теперь поля packed структур захватываются по ссылке одинаково вне зависимости от того, насколько выровнены поля.▪️Насчёт
repr
: теперь можно явно писать #[repr(Rust)]
▪️Поменяли линты о приватных определениях внутри публичных определений (например, публичная функция, возвращающая приватный тип). Старый линт (private_in_public) страдал от того, что принимал во внимание исключительно номинальную видимость — ту, которая ставится перед именем (тип pub(in foo)
). Из-за этого линт имел формально простое, но довольно неинтуитивное поведение, причём ещё и неполное — из-за вывода типов приватный тип мог утечь способом, который старый линт не ловил. Теперь его заменили пачкой новых линтов, которые работают на эффективной видимости, т. е. принимая во внимание видимость объемлющих определений. Это даёт более полезное для людей поведение. Подробнее в соответствующем RFC.▪️Насчёт линтов: задавать их теперь можно через секцию в манифесте Cargo.toml. С учётом того, что эта секция наследуется в workspace, это позволяет убедиться, что в группе связанных проектов используется идентичный набор глобальных линтов, без необходимости синхронизировать их руками.
▪️
cargo clean
теперь поддерживает --dry-run
.▪️Стабилизировали пачку API в стандартной библиотеке, в том числе:
🔸core::num::Saturating — адаптер для примитивных числовых типов, реализующий насыщающую семантику для арифметических операций:
assert_eq!(Saturating(u32::MAX) + Saturating(1), Saturating(u32::MAX));🔸пачку методов для перевода в/из байты для
OsStr{, ing}:
as_encoded_bytes
/from_encoded_bytes_unchecked
. Ранее это было возможно только на Unix-системах через std::os::unix::ffi::OsStrExt
.🔸Реализации
From
из ссылок (обоих видов) на массивы в векторы и из массивов в {Arc, Rc}<[T]>
.Дополнительно следующие API теперь могут быть использованы в константном контексте:
🔸
core::mem::transmute_copy
🔸str::is_ascii
, [u8]::is_ascii
😙👌▪️Как я уже писал,
Cell::swap
теперь паникует на частично перекрывающихся значениях.▪️rustdoc теперь позволяет добавлять свои CSS-классы к блокам кода и отдельные блоки для предупреждений.
▪️В сгенерированной rustdoc документации теперь можно искать с использованием типовых параметров.
Для примера, это означает, что
Option::or
можно найти по запросуoption<T>, option<T> -> option<T>
👍8❤🔥3
#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 бенчмаркам — причём для времени компиляции даже лучше, чем для рантайм-бенчмарков.
blog.rust-lang.org
Announcing Rust 1.75.0 | Rust Blog
Empowering everyone to build reliable and efficient software.
🔥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
.blog.rust-lang.org
Announcing Rust 1.76.0 | Rust Blog
Empowering everyone to build reliable and efficient software.
👍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
#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