Смотрю, что завезли в Rust 1.92.0 beta (релиз 11 декабря 2025) https://releases.rs/docs/1.92.0/
Что зацепило взгляд:
Минимальный LLVM 20
https://github.com/rust-lang/rust/pull/145071
В компиляторе подняли минимальную поддерживаемую версию внешнего LLVM до 20.
Если коротко, это значит:
собирать rustc со старым LLVM 19 уже нельзя;
официально поддерживаются сборки с LLVM 20 и 21;
Для пользователей rustup это просто ещё один шаг вперёд по части оптимизаций и багфиксов на стороне бэкенда.
Дальше улучшения в синтаксисе.
Allow specifying multiple bounds for same associated item, except in trait objects
PR https://github.com/rust-lang/rust/pull/146593
Этот PR почти полностью снимает ограничение ошибки E0719 (An associated type value was specified more than once https://doc.rust-lang.org/stable/error_codes/E0719.html) и позволяет несколько раз ограничивать один и тот же ассоциированный элемент трейта внутри одного T: Trait<…>, кроме случая с dyn Trait.
Что раньше было нельзя
Примеры из описания PR, которые раньше ломались с E0719:
// было запрещено:
T: Trait<Gat<u32> = u32, Gat<u64> = u64>
T: Iterator<Item = u32, Item = i32>
T: Iterator<Item = u32, Item = u32>
T: Iterator<Item: Send, Item: Sync>
T: Trait<ASSOC = 3, ASSOC = 4>
T: Trait<ASSOC = 3, ASSOC = 3>
Разрешённые обходы выглядели так:
// обходы:
T: Trait<Gat<u32> = u32> + Trait<Gat<u64> = u64>
T: Iterator<Item = u32> + Iterator<Item = i32>
T: Iterator<Item = u32> // вместо дубля
T: Iterator<Item: Send + Sync>
T: Trait<ASSOC = 3> + Trait<ASSOC = 4>
T: Trait<ASSOC = 3>
То есть формально всё можно было выразить, но запись становилась более шумной.
Еще примеры «до» и «после».
GAT-ы (Generic Associated Types) раньше приходилось "колхозить" вот так:
// раньше приходилось писать так:
fn foo<T>()
where
T: Trait<Gat<u32> = u32> + Trait<Gat<u64> = u64>
{}
// а можно будет компактнее:
fn foo<T>()
where
T: Trait<Gat<u32> = u32, Gat<u64> = u64>
{}
Пример с trait bounds на ассоциированный тип:
// было:
fn foo<T>()
where
T: Iterator<Item = u32> + Iterator<Item: Send + Sync>
{}
// а можно будет так:
fn foo<T>()
where
T: Iterator<Item = u32, Item: Send + Sync>
{}
По смыслу это то же самое, что:
fn foo<T>()
where
T: Iterator,
<T as Iterator>::Item = u32,
<T as Iterator>::Item: Send + Sync,
{}
Просто теперь можно всё это собрать в одном Iterator<...>.
Но для dyn Trait всё ещё нельзя обойти E0719
Для dyn Iterator<Item = u32, Item = u32> и аналогичных dyn Trait<…> ошибка E0719 остаётся. В описании PR есть ссылка на комментарий, где это объясняется:
Types like
dyn Iterator<Item = u32, Item = u32>will continue to be rejected, however. See #143146 (comment) https://github.com/rust-lang/rust/pull/143146#issuecomment-3274421752 for the reason why.
Если коротко, проблема в следующем:
generic-bound’ы не создают новый «реальный» тип наподобие
dyn Iterator<…>, а всего лишь накладывают условия на уже существующий тип-параметр.А
dyn Iterator<…>— это уже конкретный тип времени выполнения с vtable, layout иTypeId. Внутри компилятора он хранится как «трейтовый объект + список ограничений на ассоциированные типы».
У dyn Trait все штуки вида Item = ... входят в само тождество типа: внутренне это «трейтовый объект + список projection bounds». После PR https://github.com/rust-lang/rust/pull/136458 компилятор больше не сливает дубликаты этих bounds «потом», на поздних стадиях.
Если сейчас разрешить записи вроде dyn Iterator<Item = for<'a> fn(&'a ()), Item = for<'b> fn(&'b ())>, то такой тип станет другим по сравнению с интуитивно эквивалентным dyn Iterator<Item = for<'c> fn(&'c ())>, просто потому что список bounds отличается.
Дополнительно это лезет в разницу между старым и новым trait solver’ами: старый просто берёт первый кандидат для Item, новый пытается учесть все и слить их, если результат совпадает. При нескольких Item = ... это может привести к тонким случаям, где старый solver успешно нормализует тип, а новый внезапно видит «лишние» ограничения на лайфтаймы и объявляет неоднозначность.
Top comments (0)