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)
Ответ на: комментарий от RazrFalcon

Насколько я понимаю, не просто значений, а указателей на них.

А говорил, что не понимаешь.

Это не официальная дока

https://doc.rust-lang.org/stable/rust-by-example/

Это же официальный сайт?

Так что не нужно говорить за всех

Не нужно придираться к словам, сказав «ничерта», я утрировал, разумеется. Ты хочешь сказать, что начал знакомство с Rust именно с «Rust by Example», и после него сразу смог читать и понимать весь код на нём?

Одна из моих любимейших фич

В чём прикол?

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

Не нужно придираться к словам, сказав «ничерта», я утрировал, разумеется. Ты хочешь сказать, что начал знакомство с Rust именно с «Rust by Example», и после него сразу смог читать и понимать весь код на нём?

Неужели непонятно, что сравнение «Rust by Example» и «Go tour», в том плане, что после первого остаются непонятые моменты, когда после второго уже как бы освоил язык, некорректно, ибо семантика Rust намного шире, чем семантика Go.

В чём прикол?

Прикол в том, что указание return перед выражением в конце функции излишне ибо это конец функции как никак.

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

Ты хочешь сказать, что начал знакомство с Rust именно с «Rust by Example», и после него сразу смог читать и понимать весь код на нём?

Ещё раз: после C++ там учить нечего. Потребовалось время для понимания лайфтаймов и их «наследования» ('a: 'b). Но в целом никакой боли не вызывало. При этом мои волосы стали гладкими и шелковистыми, ибо Rust пофиксил всё, что бесило меня в C++.

В чём прикол?

Очень удобно. Убирает лишний синтаксический (?) мусор.

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

В этом нет ничего неочевидного

Неочевидным становится код. Без ретурна не всегда сразу видно что именно вернётся.

А... видишь ли, синтаксис здеь не причем. Это семантика

Вот только речь об операторе.

И нет, мнение того, кто не имеет опыта работы с обоими типами языков, никакой ценности не представляет

А ты дерьмо пробовал? Допускаешь вероятность наличия у него приятного вкуса?

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

Без ретурна не всегда сразу видно что именно вернётся.

А выражения вроде a + b тебе тоже трудно понять без return?

А... видишь ли, синтаксис здеь не причем. Это семантика

Вот только речь об операторе.

Нет.

А ты дерьмо пробовал?

Нет. Считаешь, стоит?

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

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

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

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

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

Потребовалось время для понимания лайфтаймов и их «наследования».

Я, признаться, до сих пор не понимаю конструкции

T: 'a
и
for <'a>

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

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

Нет, это всегда так за исключением функций, которые вообще не возвращают управление.

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

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

«Чёрточек» и скобочек тоже не мало, пусть даже они и означают везде одно и то же.

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

T: 'a

Это синоним 'b: 'a, насколько я понимаю. В либе что я сейчас пишу такого полно.

То есть T должен жить как минимум не меньше 'a. И не важно содержит он сам lifetime или нет.

Как компилятор это определяет - хз. Но работает.

https://doc.rust-lang.org/book/second-edition/ch19-02-advanced-lifetimes.html...

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

после C++ там учить нечего

Преувеличиваешь. Если так, то и про Go после С можно сказать так же. Есть некоторые странные вещи, но в целом ничего особенного.

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

Go мне тупо не нужен, ибо он убог и смысла его использовать я не вижу.

C, опять же, не простой, а примитивыный. На С писать намного сложнее, чем на Rust.

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

Это тоже. И это хорошо, в некотором роде это преимущество. Для всего свой инструмент.

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

про Go после С можно сказать так же

В Go есть вещи, которых нет в Си (лямбды, каналы, receiver, interface), Так что нет, нельзя.

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

T: 'a - тип T не содержит ссылок, живущих меньше 'a

for<'a> - тип, в котором используется 'a, валиден для любого времени жизни.

for<'a> fn(&'a str) - указатель на функцию принимающую ссылки с любым временем жизни. По правилам lifetime elision можно писать просто fn(&str)

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

Есть некоторые странные вещи, но в целом ничего особенного

В Go есть вещи, которых нет в Си (лямбды, каналы, receiver, interface)

Кого-то могут сильно удивить лямбды?

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

Лямбды посложнее implicit return будут, но вас это не смущает.

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

А ты дерьмо пробовал?

Нет. Считаешь, стоит?

Если ты очень хочешь чтобы твоё мнение о нём представляло ценность, то да, безусловно.

Совершенно не хочу.

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

В Go есть вещи, которых нет в Си (лямбды, каналы, receiver, interface)

Кого-то могут сильно удивить лямбды?

Того, кто к ним не привык. То же и по другим пунктам.

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

А раст после плюсов на 100% привычен и понятен? Об этом же речь и была. Мой пример про C и Go как раз должен был это скорее опровергнуть.

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

А знаки препинания помогают сделать текст более кратким

Ну дык, а то хранение исходников это такая проблема из-за их объёма.

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

А раст после плюсов на 100% привычен и понятен?

Если и не на 100%, то на 90%. С одним дополнением - приходится объяснять компилятору лайфтаймы (но неформально лайфтаймы есть и в Си, и в Си++).

tailgunner ★★★★★
()

Что? Да пусть будет проклят кто напишет такой код:

pub fn foo(a: &[u8], b: &[u8], c: &mut [u8]) { for ((a, b), c) in a.iter().zip(b).zip(c) { *c = *a + *b; } }

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

Всем остальным редакторам которые я когда либо видел. Даже в nano есть.

Ну так это «все остальные» редакторы без Qt писаны. Даже nano. Ты уже забыл, наверное, что твой любимый Qt можно легко заменить Electron'ом. Я же ведь об этом говорю, а не о спэлл-чекере в VS Code или где-то там.

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

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

Не особенно хорошо знаком с lua, но кроме вызова функции с помощью словаря (таблицы или как оно в луа) я там дичи не видел, не зря же его все кому не лень юзают для плагинов и конфигов.

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

Ну не только из новостей, но да — только примеры.

hobbit ★★★★★
()

Возня какая то. Когда уже будет нормальный безопасный параллельно-векторизированный ЯП для браузеров?!

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

Что? Да пусть будет проклят кто напишет такой код:
pub fn foo(a: &[u8], b: &[u8], c: &mut [u8]) { for ((a, b), c) in a.iter().zip(b).zip(c) { *c = *a + *b; } }

Ты еще пробелы убери для большей драмы.

Да всеравно...

Ясно-понятно.

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

У некоторых в приоритете надёжность, а не простота написания.

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

А что не так? Главное достижение Эйнштейна это «жонглирование индексами». Когда сложное становится простым. А такое вот читать. Извините....3 страницы комментов что оно делает...

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