August 23rd, 2016

satyr

Вернуть итератор

А между тем, зацените, в nightly rust научились делать вот так:

#![feature(conservative_impl_trait)]

fn numbers() -> impl Iterator<Item = i32> {
    1 ..
}

Дословно, мы из функции возвращаем некоторый анонимный тип, всё что известно про которого — это то, что он реализует типаж Iterator, ассоциативный тип Item коего установлен в i32.

Выглядит сумбурно, но знающие люди будут со мной солидарны в том, что это единственная вещь, которой не хватало для того, чтобы итераторы было реально удобно использовать. Общеизвестно, что автовывод типов в Rust работает только внутри функции, поэтому нельзя было малой кровью собрать лесенку итераторов и вернуть результат наружу, без обязательного прописывания получившегося типа в сигнатуре. До сих пор в этих случаях людям приходилось использовать либо trait objects:

fn numbers() -> Box<Iterator<Item = i32>> {
    Box::new(1 ..)
}

… что плохо, потому что здесь одна совершенно ненужная аллокация, и итерация через vtable. Либо писать вот так:

fn numbers() -> std::ops::RangeFrom {
    1 ..
}

… что тоже омерзительно, по ряду причин:
  • Надо знать возвращаемый тип, а он может быть сильно составным (и будет, в случае с лесенкой преобразований итератора).
  • Зачастую приходится оборачивать его в newtype, чтобы не экспортировать из модуля кишки (и имплементировать соответствующий Deref).
  • В некоторых случаях тип вообще невозможно прописать руками, например, когда generic биндится замыканием (а тип замыкания нельзя написать руками).

Теперь же можно совершенно спокойно возвращать анонимные типы, описав трейтами что, собственно, с этими типами можно будет делать, и компилятору этого будет достаточно, чтобы проверить, что программа написана корректно.

Помимо итераторов, данная возможность даст возможность возвращать замыкания перемещением (by move), как-то так:

#![feature(conservative_impl_trait)]

fn adder(a: i32) -> impl Fn(i32) -> i32 {
    move |x| x + a
}

Это ваще космос. По факту можно будет с относительным комфортом писать в функциональном стиле на языке, в котором нет garbage collector (!!), и при этом без сегфолтов и утечек.

Да, и ещё формат ошибок в Rust теперь слизали с Elm, и они теперь выглядят максимально подробно:


Короче, я повторю свою мысль ещё раз: в 2016-м не может быть ни единой причины писать что-то на C++, когда есть Rust. Ну, может, если вы только дорабатываете какой-то уже существующий большой проект. Начинать же сейчас что-то новое на плюсах вообще безумие, никогда так не делайте :)