1.83K subscribers
3.24K photos
127 videos
15 files
3.52K links
Блог со звёздочкой.

Много репостов, немножко программирования.

Небольшое прикольное комьюнити: @decltype_chat_ptr_t
Автор: @insert_reference_here
Download Telegram
#prog #rust #rustreleasenotes

Вышла версия Rust 1.69.0! Как всегда, расскажу только о том, что интересует меня, а об остальном читайте в ченджлоге.

▪️Создание невыровненных ссылок на поля packed-структур наконец-то является ошибкой компиляции, которую нельзя подавить линтом.

▪️Так как изменение выше сломало некоторый (очень) старый код, derive-макросы теперь имеют меньше ограничений при использовании на packed типах.

▪️В union теперь можно использовать типы, определённые через ассоциированные типы трейтов. Даже странно, что раньше было нельзя.

▪️Методы трейтов не могут быть вызваны на dyn-объектах, если у них есть ограничения на Self. В принципе, это логично, поскольку апкаст до трейт-объекта стирает тип, и определить в рантайме, реализовывал ли изначальный тип трейт, нельзя. С другой стороны, auto-трейты не имеют никаких методов и потому не добавляют новых записей в vtable. Начиная с этой версии Rust, на трейт-объектах можно вызывать методы трейтов с баундами вида where Self: AutoTrait. При этом вызывать можно будет только для трейт-объектов, у которых через + эти auto-трейты указаны.

Иными словами, если

trait Trait {
fn foo(&self) where Self: Sync;
}


, то foo можно вызвать на dyn Trait + Sync, но нельзя на dyn Trait.

▪️До релиза докатился фикс бага компилятора (&*).

▪️Поиск в rustdoc теперь ищет лишь макросы, если запрос оканчивается на !.

▪️Стабилизировали CStr::from_bytes_until_nul, который сам ищет первый nul-байт в переданном слайсе (и возвращает ошибку, если их нету вовсе).

▪️Cargo теперь по умолчанию собирает build-зависимости без отладочной информации, если они потом не используются для сборки итогового бинаря как runtime-зависимости. Автор провёл замеры и показал, что это действительно ускоряет компиляцию.
👍124💩1
#prog #rust #rustreleasenotes

Вышла версия Rust 1.70.0! Как обычно, тут лишь избранные куски, а полный ченджлог по ссылке.

▪️Раньше код вида let _ = <some expr>; компилировался, даже если в <some expr> была небезопасная операция вне unsafe-блока [1][2] или const-ошибка. Это происходило из-за того, что этот анализ проходил на уровне MIR, а подобные statement-ы вообще ни во что не раскрывались при переводе в MIR. Теперь это пофиксили.

▪️Как известно, использование глоб-реэкспортов может привести к неоднозначностям и даже к невозможности использовать экспортированное API. Теперь на это дело добавили линт.

▪️При включённых отладочных ассертах компилятор теперь вставляет проверки выравнивания при разыменовывании сырых указателей.

▪️cargo теперь использует sparse протокол по умолчанию. Из недостатков — скачиваемые зависимости при этом хранятся по другому пути и потому при первой компиляции будут скачиваться заново.

▪️cargo update для удовлетворения требований версий зависимостей может иногда даунгрейдить зависимости. Теперь об этом сообщается более явно.

▪️При сборке проекта теперь доступна переменная окружения CARGO_PKG_README с путём к README-файлу для проекта.

▪️concat! теперь работает с негативными числовыми литералами. Да, я тоже удивлён, что раньше не мог.

▪️Ещё одно изменение докатилось до стейбла (при этом многопоточную версию назвали OnceLock).

▪️NonZero*-типы получили ассоциированные константы MIN и MAX.

▪️Option::filter стабилизировали... Но под страшненьким именем is_some_and (а также Result::is_ok_and и Result::is_err_and).

▪️Стабилизировали Rc::into_inner и Arc::into_inner. Они возвращают Some(T), если искомый экземпляр был последней сильной ссылкой на T, и None в противном случае. Конкретно для Rc это эквивалентно Rc::try_unwrap(x).ok(), а вот для Arc дело немного хитрее. Аналогичный, казалось бы, код Arc::try_unwrap(x).ok() подвержен состоянию гонки: если исполнение потоков планировщиком перекроется так, что два потока оба вызовут Arc::try_unwrap до вызова .ok(), то оба вызова вернут Err (так как сильная ссылка не единственная) и оба потока в .ok() дропнут экземпляры Arc. Если на данные оставались только эти две сильные ссылки, то так можно с концами эти данные потерять. Arc::into_inner лишён этой ловушки: гарантируется, что если этот метод будет вызван на всех экземплярах конкретного Arc (в том числе и из нескольких потоков), то ровно один из этих вызовов вернёт Some, т. е. данные потеряны не будут.

▪️Метод retain добрался и до кучи.

▪️NonNull<[T]> теперь можно составить из NonNull<T> и длины.

▪️Куча итераторов теперь реализует Default. Значения по умолчанию при это не возвращают никаких элементов.

===========================

Из смешного: RELEASES.md разросся до таких размеров, что для некоторых пользователей GitHub не осиливает его отрендерить.
👍8🔥3🖕2🤝1
Блог*
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 #моё

Что ж, наверное, стоит объяснить, а что это вообще за код такой.

Странно выглядят фрагменты вроде

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). Но это обобщённая функция — так какой же тип там выводится?
1😐1
Ну а теперь серьёзно.

#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.
👍8🎉21👎1
#prog #rust #rustreleasenotes

Вышла версия 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.

В целом довольно минорный релиз, сильных причин обновлять нету.

▪️Текущее поведение компилятора — считать 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 в ссылку.
👍91
#prog #rust #rustreleasenotes

Вышла версия 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 значениях с проверкой полноты покрытия без _, а также может матчиться по (частично) перемещённым значениям, если паттерны не смотрят на значение. Это поведение консистентно с поведением let _ = expr;.

▪️Метод std::split_inclusive теперь возвращает итератор, который корректно реализует DoubleEndedIterator в том плане, что возвращает одни и те же кусочки строки при итерации как в прямом, так и в обратном порядке.

▪️Стабилизированы некоторые новые API, в частности:
🔸Конструкторы ссылок на Atomic-типы из сырых указателей (unsafe, разумеется)
🔸Пачку API (включая платформо-специфичные) для манипуляции временны́ми атрибутами файлов
🔸Битовые операции над IP-адресами
🔸Option::as{, _mut}_slice.
🔸Пачку методов для сырых указателей с указаниями смещений в байтах, а не в размерах указываемого типа.

Также в const контексте теперь можно вызывать:
🔸
MaybeUninit::assume_init_read
🔸
MaybeUninit::zeroed
🔸
mem::discriminant
🔸
mem::zeroed

Насчёт Option::as_slice у вас мог возникнуть резонный вопрос, чем этот метод лучше opt_value.map_or(&[], std::slice::from_ref). Дело в том, что такой прямолинейный метод будет использовать некий посторонний адрес для случая, когда значение является None, а новый метод старается по возможности вернуть ссылку на слайс с адресом, совпадающим с адресом исходного опционального значения.

▪️Некоторые улучшения rustdoc:
🔸Генерируется предупреждение, когда трейт не является object safe.
🔸Атрибут #[repr(transparent)] скрывается, если поле, над которым тип является transparent, является приватным (поскольку в этом случае repr наверняка является деталью реализации)
🔸Для C-like (с вариантами без полей) enum теперь показываются дискриминанты вариантов.

▪️Компилятор (конкретно librustc_driver) теперь оптимизируется при помощи BOLT — специального инструмента для оптимизации скомпилированных программ, который переставляет инструкции для лучшей утилизации кеша. Даёт прибавку в скорости.

▪️Маленькие функции теперь автоматически инлайнятся через границу крейтов. Дало большой выигрыш и по primary, и по secondary бенчмаркам — причём для времени компиляции даже лучше, чем для рантайм-бенчмарков.
🔥8❤‍🔥3👍2🎉1
#prog #rust #rustreleasenotes

Вышла версия Rust 1.76.0... Почти месяц назад. В свою защиту могу сказать, что изменения довольно минорные и потому релиз не обязывает к апдейту. Как обычно, тут только отдельные моменты, целиком в release notes.

▪️Единственная существенная вещь: исправлены ошибки при работе с unsized #[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.
👍81