LINUX.ORG.RU

Rust 1.27

 


3

10

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

Основные изменения:

  • SIMD — наиболее значимое и ожидаемое нововведение: стабильная версия Rust обзавелась базовой поддержкой SIMD.
    Для примера использования рассмотрим следующий сниппет:
    pub fn foo(a: &[u8], b: &[u8], c: &mut [u8]) {
        for ((a, b), c) in a.iter().zip(b).zip(c) {
            *c = *a + *b;
        }
    }
    
    Здесь мы берем два слайса, складываем числа в них и помещаем результат в третий слайс. Самый простой способ, описанный выше — это проход в цикле по каждому слайсу, сложение и сохранение результата. Впрочем, это можно сделать быстрее и в LLVM может автовекторизовать такой код, подставив SIMD инструкции автоматически.

    Стабильный Rust уже давно использует возможности автовекторизации LLVM, но, к сожалению, компилятор может использовать подобные оптимизации не всегда. В Rust 1.27.0 добавлен модуль std::arch, включающий базовую поддержку использования инструкций SIMD напрямую из кода на Rust. Кроме того, добавлены соответствующие директивы условной компиляции:

    #[cfg(all(any(target_arch = "x86", target_arch = "x86_64"),
          target_feature = "avx2"))]
    fn foo() {
        #[cfg(target_arch = "x86")]
        use std::arch::x86::_mm256_add_epi64;
        #[cfg(target_arch = "x86_64")]
        use std::arch::x86_64::_mm256_add_epi64;
    
        unsafe {
            _mm256_add_epi64(...);
        }
    }
    
    В примере выше флаги cfg позволяют выбрать правильную версию функции в зависимости от целевой архитектуры во время компиляции. Также мы можем это сделать в рантайме:
    fn foo() {
        #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
        {
            if is_x86_feature_detected!("avx2") {
                return unsafe { foo_avx2() };
            }
        }
    
        foo_fallback();
    }
    

    Примитивы, включенные в стандартную библиотеку обеспечивают минимальную поддержку и весьма низкоуровневы, но их включение позволяет развивать более выкокоуровневые примитывы в библиотеках. К примеру, крейт faster предоставляет удобный интерфейс, основанный на концепции итераторов.

    Без SIMD

    let lots_of_3s = (&[-123.456f32; 128][..]).iter()
        .map(|v| {
            9.0 * v.abs().sqrt().sqrt().recip().ceil().sqrt() - 4.0 - 2.0
        })
        .collect::<Vec<f32>>();
    

    С SIMD (faster)

    let lots_of_3s = (&[-123.456f32; 128][..]).simd_iter()
        .simd_map(f32s(0.0), |v| {
            f32s(9.0) * v.abs().sqrt().rsqrt().ceil().sqrt() - f32s(4.0) - f32s(2.0)
        })
        .scalar_collect();
    

  • dyn Trait

    Изначальный синтаксис трейт-объектов в Rust — одна из тех вещей, о введении которых мы жалеем: для некоторого трейта Foo, его трейт-объект будет выглядеть так: Box<Foo>

    И если Foo является структурой, синтаксис аллокации структуры в «куче» будет выглядеть точно так же.

    Когда язык разрабатывался, казалось что сходство здесь уместно, но опыт показал, что это только путает людей.

    То же самое справедливо для случая impl SomeTrait for SomeOtherTrait, что является корректным синтаксисом, но интуитивно понимается как реализация трейта SomeTrait для всех типов, реализующих SomeOtherTrait, но на самом деле это записывается как impl<T> SomeTrait for T where T: SomeOtherTrait, в то время как первый вариант является реализацией трейта для трейт-объекта.

    Кроме того, появление impl Trait в противовес Trait ввело некую неконсистентность при обучении языку.

    Исходя из этого, в Rust 1.27 мы стабилизируем новый синтаксис dyn Trait.
    Теперь трейт-объейты выглядят так:

    // old => new
    Box<Foo> => Box<dyn Foo>
    &Foo => &dyn Foo
    &mut Foo => &mut dyn Foo
    

    То же самое применимо к другим типам указателей:
    Arc<Foo> теперь объявляется как Arc<dyn Foo>.

    Исходя из требований к обратной совместимости, мы не можем удалить старый синтаксис, но мы добавили предупреждение компилятора, призывающее обновить синтаксис для трейт-объектов. Пока что оно выключено по умолчанию.

  • #[must_use] для функций
    Наконец, атрибут #[must_use] получил новые возможности: теперь он применим к функциям.

    Раньше он использовался только на типах, таких как Result<T, E>, где возвращаемое значение должно быть использовано, но теперь возможно такое применение:

    #[must_use]
    fn double(x: i32) -> i32 {
        2 * x
    }
    
    fn main() {
        double(4); // warning: unused return value of `double` which must be used
    
        let _ = double(4); // (no warning)
    }
    

    Также этот атрибут был добавлен к некоторым функциям в стандартной библиотеке, таким как Clone::clone, Iterator::collect и ToOwned::to_owned, теперь компилятор предупредит в случае, если их результат останется неиспользованным, что поможет заметить и предотвратить случайный вызов затратных операций.

Стабилизация стандартной библиотеки

Новые возможности Cargo

В текущем релизе в Cargo включены два маленьких нововведения:

Во-первых, Cargo теперь принимает флаг --target-dir.

Во-вторых, Cargo теперь будет пытаться автоматически найти тесты, примеры и исполняемые файлы (прим. бинарные подпроекты в библиотечных крейтах) в проекте, но явная конфигурация всё же будет требоваться в некоторых случаях.

Для помощи в конфигурации мы добавили несколько ключевых слов в Cargo.toml.

Обновить Rust можно с помощью команды:

curl https://sh.rustup.rs -sSf | sh # если у вас еще не установлен rustup
rustup update stable

>>> Подробности

★★★★★

Проверено: tailgunner ()
Последнее исправление: cetjs2 (всего исправлений: 5)
Ответ на: комментарий от WitcherGeralt

Решение в расте как раз исправляет несовместимость старых языков с лямбдами. Вот есть лямбда

a => foo(bar(a, 1))
и всё отлично. Но потом что-то глючит и хочется посмотреть промежуточный результат в дебаге. Делаем 2 инструкции
a => {
var bar_res = bar(a, 1);
return foo(bar_res);
}
Подебажили, вертаем взад - опять удалять return. Аналогично при копипасте похожего кода, половина без ретурна, половина с ретурном.

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

khrundel ★★★★
()
Ответ на: комментарий от WitcherGeralt

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

red75prim ★★★
()
Ответ на: комментарий от gene1

Не, пишу на них раз в 5 лет по большой необходимости, как следствие толком и не умею.

WitcherGeralt ★★
()
Ответ на: комментарий от WitcherGeralt

Ты действительно считаешь, что лучше задефайнить целую длинную перегрузку чтобы просто заматчить null? Я предпочту ретурн.

Прием такой, что делаем публичную функцию, например ту же extract, которая вызывает множество вариантов приватных функций с тем же именем, но разными сигнатурами. При этом if-else не нужен ни в публичной функции, не в приватных.

И часто дело не заканчивается обычным null, а например, проверянием наличия нужных ключей и значений в ассоциативном массиве. Можно считать это динамичной типизацией, особенно актуально во всяких веб-серверах, когда в запросе может быть практически все что угодно, как нужное, так и нет. Это позволяет сделать функцию «где все задано правильно», а логику обработки исключений вывести в соседние функции.

Если в Расте когда-либо будет value-based type, может это будет работать так же. Чисто статическая же типизациая малопригодна, потому что реквесты приходят в одних и тех же типах, а корректность данных обусловлена ее динамической структурой (например, всегда приходит ассоциативный массив, но иногда там есть нужный ключ, а иногда нет).

gene1
()
Ответ на: комментарий от red75prim

И чем же тернарный оператор такой замечательный?

Ничем кроме компактности записи, лично мне он чисто эстетически нравится.

Используется в нескольких языках, поэтому понятен всем, знакомым с этими языками?

А ты знаешь много программеров совсем не знакомых с Си? Передай им чтобы шли лучше в дворники.

WitcherGeralt ★★
()
Ответ на: комментарий от gene1

Это позволяет сделать функцию «где все задано правильно»

Ну если так, то да, очень нужно, но с нулами пример был паршивый.

WitcherGeralt ★★
()

Эх, запилили бы общее решение вместо костыля async/await... Для примера подражания можно было бы использовать workflows из F#.

dave ★★★★★
()
Ответ на: комментарий от WitcherGeralt

Чтобы использовать одни и те же методы для разных типов

Для этого существует параметрический полиморфизм. Перегрузка же существует для разных данных. Например:

fact 1 = 1

fact n = n * fact $ n - 1

Все остальные её применения - однозначные костыли.

Nexmean
()
Ответ на: комментарий от tailgunner

В процедурном стиле ещё часто любят делать несколько ретурнов, из разных участков функции. Что ни говори, а часто такая возможность полезна. Как пишут в Rust в подобных случаях?

Crocodoom ★★★★★
()
Ответ на: комментарий от Crocodoom

Также и пишут return something;, при этом такая строчка имеет тип выражения !, который является ненаселённым типов.

Nexmean
()
Ответ на: комментарий от Crocodoom

Честно говоря ни того, ни другого, это просто матчинг по параметрам функции. Но перегрузка в том смысле, в котором она есть в продурщине не нужна.

Nexmean
()
Ответ на: комментарий от MyTrooName

У раста тоже приколов хватает. Но их активно фиксят. Теперь вот dyn Trait сделали. Скоро ещё макросы через use можно будет импортировать, а не через одно место, как сейчас.

lisp

Шутка удалась. АСТ не нужен.

lua

Не пользовался. Но знаю что там массивы с 1 начинаются. Чем не дичь?

Всё остальное из списка мертво.

RazrFalcon ★★★★★
()
Последнее исправление: RazrFalcon (всего исправлений: 1)
Ответ на: комментарий от RazrFalcon

C++ виноват. И то, что вторая половина модулей это попытка встроить в тулкит свой electron с разметкой и стилями (qml) - тоже вина C++

makoven ★★★★★
()
Последнее исправление: makoven (всего исправлений: 1)
Ответ на: комментарий от makoven

QML к С++ никакого отношения не имеет, ибо он декларативный.

По факту: используем жирный, старый Qt или в 10-ки более жирный и тормозной, но новый электрон.

RazrFalcon ★★★★★
()
Последнее исправление: RazrFalcon (всего исправлений: 1)
Ответ на: комментарий от RazrFalcon

А что там кстати дальше, за элекроном? Ничего пока не придумали еще более жирного, но «удобного и быстрого»?

anonymous
()
Ответ на: комментарий от anonymous

Java? Как минимум IDEA жрёт и тормозит в разы больше VSCode.

RazrFalcon ★★★★★
()
Ответ на: комментарий от RazrFalcon

или в 10-ки более жирный и тромозной, но новый электрон

А все-таки, почему бы не использовать браузер?

Пишешь на том же расте свой обезжиреватель svg со встроенным rest-сервером (+ отдачей статики). При нажатии на иконку с установленной твоей програмой запускается консоль в которой написано «введите в браузере htttp://localhost:1488». А еще лучше чтоб сразу страничка открывалась в браузере по-умолчанию. Работать будет везде - даже на андроиде

makoven ★★★★★
()
Ответ на: комментарий от MyTrooName

К примеру **kwargs.

а что с ним не так?

В питоне предпочитают слова закорючкам, а **kwargs несколько выбивается из этого правила. В том же питоне2 было больше закорючек, например `var` или print >>out, var, в питоне3 это поубирали.

Crocodoom ★★★★★
()
Ответ на: комментарий от Crocodoom

Какую-то закорючку все-равно бы пришлось мутить. В js сделали ...theArgs. Особой разницы нет. Ну или вовсе запретить ловить обездоленные аргументы, вылетая с ошибкой — я бы проголосовал за этот пункт

makoven ★★★★★
()
Последнее исправление: makoven (всего исправлений: 2)
Ответ на: комментарий от RazrFalcon

Сишкой пахнет.

Согласен про PTSD или еще какой-нибудь disorder.

В псевдокоде такого быть не должно.

Пока ты будешь считать Python псевдокодом, у тебя будут трудности с его изучением.

tailgunner ★★★★★
()
Ответ на: комментарий от RazrFalcon

К примеру **kwargs.

Это не дичь, а крайне юзабельная штука.

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

WitcherGeralt ★★
()
Ответ на: комментарий от MyTrooName

Tcl — сам по себе одна большая дичь, про паскаль даже начинать не буду, а лиспом, ты, видимо, толсто троллишь.

WitcherGeralt ★★
()
Ответ на: комментарий от WitcherGeralt

Ну так я использую его по назначению - для мелких скриптов. Писать многопоток на нём мне бы и в голову не пришло.

PS: Ругать питон за GIL равносильно отсутствию дженериков в Go.

RazrFalcon ★★★★★
()
Ответ на: комментарий от RazrFalcon

Потому, что SaaS не нужен

Браузер — новый qt. Смирись. В qt есть смысл для какой-нибудь krita или shotcut. Там где мышинная возня во все поля и скорость отрисовки критична. В твоем случае qt используется как, пусть и мудреная, но форма ввода для cli утилиты

Встроив в cli-утилиту веб-сервер можно сделать програмку to rule them all: и сохранится работа из командной строки, и gui и (приложив рядом systemd-юнит) сервис, и SaaS

makoven ★★★★★
()
Последнее исправление: makoven (всего исправлений: 1)
Ответ на: комментарий от Nexmean

Перегрузка же существует для разных данных

А я что сказал? Ты не доцитировал. Даже если и костыли, всё равно нужно.

WitcherGeralt ★★
()
Ответ на: комментарий от makoven

Встроив в cli-утилиту веб-сервер можно сделать програмку to rule them all

Но почему так никто не делает? разве что syncthing таскает внутри себя сайт.

tailgunner ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.