Rust 1.65

Rust 1.65
Изображение rust-lang.org
👋
Хочешь поучаствовать в жизни сайта? Мы ищем авторов!

Ассоциированные типы теперь можно обобщать, паттерн-матчинг в let - делать необязательным, а из блоков {} можно вываливаться раньше времени с помощью break.

Пройдемся по ченджлогу свежей версии языка.

Обобщенные ассоциированные типы

Или Generic Associated Types, или GAT. Другими словами, ассоциированные типы теперь могут быть обобщены на другие типы, времена жизни (lifetime) и константы:

trait Foo {
    type Bar<'x>;
}

Примеры использования GAT:

/// Trait наподобие `Iterator`, который может заимствовать у самого себя
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>;
}

/// Позволяет заимствовать массивы. Удобно для структур,
/// которые могут хранить данные непоследовательно.
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 можно почитать в блоге Rust:

let-else

Новый синтаксис пытается присвоить значение справа шаблону слева, и если шаблон не подходит, выполняет код в блоке else:

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

else может содержать, например, break, return, panic!.

Прежде распаковывать шаблоны в let можно было, только если компилятору статически было известно, что шаблоны всегда будет матчиться значению справа. Для let-else это необязательно.

Пример использования:

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"));

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
};

Прежде разработчикам приходилось писать блок loop, который выполнялся всегда один раз, чтобы добиться похожего поведения.

Вынос отладочной информации из объектных файлов в Linux

Новые опции компилятора управляют разделением отладочной информации (debuginfo splitting):

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

В Rust 1.51 эта функция компилятора стала доступна на macOS, теперь ее добавили для Linux.

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

Эти функции теперь доступны в const-контексте:

Полный список изменений доступ в официальном блоге.

Материал подготовлен с ❤️ редакцией Кухни IT.

Олег Ямников

Олег Ямников

Главный кухонный корреспондент.