Команда Rust рада сообщить о новой версии языка — 1.65.0. Rust — это язык программирования, позволяющий каждому создавать надёжное и эффективное программное обеспечение.


Если у вас есть предыдущая версия Rust, установленная через rustup, то для обновления до версии 1.65.0 вам достаточно выполнить команду:


rustup update stable

Если у вас ещё нет rustup, то можете установить его со страницы на нашем веб-сайте, а также ознакомиться с подробным описанием выпуска 1.65.0 на GitHub.


Если вы хотите помочь нам протестировать будущие выпуски, вы можете использовать beta (rustup default beta) или nightly (rustup default nightly) канал. Пожалуйста, сообщайте обо всех встреченных вами ошибках.


Что стабилизировано в 1.65.0


Генерализованные ассоциативные типы (GAT)


Времена жизни, типы и константные обобщения теперь могут быть объявлены с ассоциированными типами:


trait Foo {
    type Bar<'x>;
}

Сложно в двух словах описать, насколько это может оказаться полезным, так что представим несколько примеров типажей, чтобы прочувствовать их силу:


/// `Iterator`-подобный типаж, который может заимствовать `Self`
trait LendingIterator {
    type Item<'a> where Self: 'a;

    fn next<'a>(&'a mut self) -> Option<Self::Item<'a>>;
}

/// Может быть реализован через умные указатели, вроде `Rc` или `Arc`,
/// так как типаж обобщён для типа указателя.
trait PointerFamily {
    type Pointer<T>: Deref<Target = T>;

    fn new<T>(value: T) -> Self::Pointer<T>;
}

/// Разрешается заимствовать массив элементов. Удобно для
/// `NdArray`-подобных типов. которым неважно непрерывное хранение данных.
trait BorrowArray<T> {
    type Array<'x, const N: usize> where Self: 'x;

    fn borrow_array<'a, const N: usize>(&'a self) -> Self::Array<'a, N>;
}

Как вы можете видеть, GAT'ы довольно универсальны и позволяют использовать ряд шаблонов, которые сейчас нельзя написать. Для получения дополнительной информации, смотрите прошлогодний пост, в котором было анонсировано принятие решения о работе над ними — или пост о стабилизации, опубликованный на прошлой неделе. Первый углубляется в приведённые выше примеры, а второй рассказывает об ограничениях текущей реализации.


Больше деталей можно найти в секции ассоциированных типов справочника nightly или в оригинальном RFC (который был открыт более 6,5 лет назад!).


let-else выражения


Данный выпуск представляет новый тип оператора let с условным шаблоном и отдельным блоком else, который исполняется при несовпадении образца.


let PATTERN: TYPE = EXPRESSION else {
    DIVERGING_CODE;
};

Обычные операторы let могут использовать только неопровержимые шаблоны, о которых статически известно, что они всегда совпадают. Этот шаблон часто представляет собой просто связывание одной переменной, но может также распаковывать составные типы, такие как структуры, кортежи и массивы. Однако это нельзя было использовать для условных совпадений, например для извлечения варианта перечисления — до сих пор! С помощью let else опровержимый шаблон может сопоставлять и связывать переменные в окружающей области видимости, как обычный let, или расходиться (как break, return, panic!), когда шаблон не совпадает.


fn get_count_item(s: &str) -> (u64, &str) {
    let mut it = s.split(' ');
    let (Some(count_str), Some(item)) = (it.next(), it.next()) else {
        panic!("Can't segment count item pair: '{s}'");
    };
    let Ok(count) = u64::from_str(count_str) else {
        panic!("Can't parse integer: '{count_str}'");
    };
    (count, item)
}
assert_eq!(get_count_item("3 chairs"), (3, "chairs"));

Область действия привязок имён — это главное, что отличает их от выражений match или if letelse. Раньше вы могли аппроксимировать эти шаблоны неудачным повторением и внешним let:


    let (count_str, item) = match (it.next(), it.next()) {
        (Some(count_str), Some(item)) => (count_str, item),
        _ => panic!("Can't segment count item pair: '{s}'"),
    };
    let count = if let Ok(count) = u64::from_str(count_str) {
        count
    } else {
        panic!("Can't parse integer: '{count_str}'");
    };

break от помеченных блоков


Простые блочные выражения теперь могут быть помечены как цель break, завершающая этот блок раньше. Это может звучать как goto, но это не произвольный переход, а только изнутри блока в его конец. Это уже было возможно с блоками loop, и вы, возможно, видели, как люди пишут циклы, которые всегда выполняются только один раз, просто чтобы получить помеченный break.


Теперь для этого появилась специальная языковая возможность! Помеченный break также может включать значение выражения, как и в случае с циклами, что позволяет блоку с несколькими операторами иметь раннее значение «возврата».


let result = 'block: {
    do_thing();
    if condition_not_met() {
        break 'block 1;
    }
    do_next_thing();
    if condition_not_met() {
        break 'block 2;
    }
    do_last_thing();
    3
};

Разделение отладочной информации в Linux


Ещё в Rust 1.51 команда компилятора добавила поддержку разделения отладочной информации в macOS — и теперь эта опция стабильна для использования и в Linux.


  • -Csplit-debuginfo=unpacked разделит отладочную информацию на несколько .dwo — объектных файлов DWARF.
  • -Csplit-debuginfo=packed создаст один DWARF-пакет .dwp вместе с вашим выходным двоичным файлом со всей отладочной информацией, упакованной вместе.
  • -Csplit-debuginfo=off по-прежнему является поведением по умолчанию, которое включает данные DWARF в .debug_* ELF-объектов и окончательный двоичный файл.

Split DWARF позволяет компоновщику избежать обработки отладочной информации (поскольку её больше нет в компонуемых объектных файлах), что может ускорить время компоновки!


Другие цели теперь также принимают -Csplit-debuginfo в качестве стабильной опции со своим значением по умолчанию для конкретной платформы, но указание других значений по-прежнему нестабильно.


Стабилизированные API


Стабилизированы следующие методы и реализации трейтов:



Особо отметим, что Backtrace API позволяет захватить трассировку стека в любое время, используя те же платформо-зависимые реализации, которые обычно используются в трассировке паники. Это может быть полезно, например, для ошибок контекста времени выполнения.


Следующие API теперь возможно использовать в контексте const:



Замечания о совместимости


  • В качестве заключительного шага отказа от RLS, в данном выпуске RLS заменяется небольшим LSP-сервером, который показывает предупреждение об отказе, советующем перейти на rust-analyzer.

Прочие изменения


Выпуск Rust 1.65 включает и другие изменения:


  • MIR inlining теперь включено для оптимизированной компиляции. Это позволяет улучшить компиляцию на 3-10% для крейтов.
  • При планировании сборок Cargo теперь сортирует очередь ожидающих заданий для повышения производительности.

Проверьте всё, что изменилось в Rust, Cargo и Clippy.


Участники 1.65.0


Многие люди собрались вместе, чтобы создать Rust 1.65.0. Без вас мы бы не справились. Спасибо!


От переводчиков


С любыми вопросами по языку Rust вам смогут помочь в русскоязычном Телеграм-чате или же в аналогичном чате для новичковых вопросов. Если у вас есть вопросы по переводам или хотите помогать с ними, то обращайтесь в чат переводчиков.


Данную статью совместными усилиями перевели andreevlex, TelegaOvoshey и funkill.

Комментарии (6)


  1. rickshaw
    04.11.2022 17:11
    +2

    Разве "generic" не переводится как "обобщенный" / "полиморфный"?


  1. Cheater
    05.11.2022 00:53
    -1

    <deleted>


  1. ababo
    05.11.2022 02:05
    +2

    Какой жирный релиз!


  1. nlinker
    05.11.2022 10:16
    +5

    Эпохальное изменение, без шуток. Для авторов библиотек это, скорее всего, самый важный релиз за этот год. Естественно, я про GATы.


  1. Helltraitor
    05.11.2022 10:44
    +2

    break для блоков напрягает - решение, видимо, просто чтобы избавиться от loop в коде

    let very very very very very very long pattern else

    Выглядит не приятно, самая важная часть в КОНЦЕ. Почему не let else pattern-tail?


    1. cheshirat
      05.11.2022 17:04
      +2

      На мой взгляд, самое важное как раз в начале (let-binding, например, let Some(v) = v else...), а в конце -- что делать, если не получилось.