DEV Community

Laruh Development Presents
Laruh Development Presents

Posted on

Что нового в Rust 1.92 beta: LLVM 20 и послабление E0719

Смотрю, что завезли в 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>
Enter fullscreen mode Exit fullscreen mode

Разрешённые обходы выглядели так:

// обходы:
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>
Enter fullscreen mode Exit fullscreen mode

То есть формально всё можно было выразить, но запись становилась более шумной.

Еще примеры «до» и «после».

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>
{}
Enter fullscreen mode Exit fullscreen mode

Пример с 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>
{}
Enter fullscreen mode Exit fullscreen mode

По смыслу это то же самое, что:

fn foo<T>()
where
    T: Iterator,
    <T as Iterator>::Item = u32,
    <T as Iterator>::Item: Send + Sync,
{}
Enter fullscreen mode Exit fullscreen mode

Просто теперь можно всё это собрать в одном 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)