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

Я в основном использую Kate, а он на Qt. Шах и мат.

твой любимый Qt можно легко заменить Electron'ом.

Нельзя, увы. При этом серьезных прог на Electron штук 5 пока.

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

А что не так?

Если тебе всё равно, есть пробелы или нет (а тебе всё равно), то ты просто парсинг-бот, не понимающий семантики.

Главное достижение Эйнштейна это «жонглирование индексами». Когда сложное становится простым.

За простотой - к Напильнику.

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

Если тебе всё равно, есть пробелы или нет (а тебе всё равно)

Да ты телепат. Я как пользователь Python оценил. Тут пробелы не помогут...

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

Приятно, но на абсолютную киллер-фичу, надо сказать, не тянет. А уж тем более на Linux, где GTK + GTK-темы нативнее и привычнее всех прочих.

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

import numpy

Уже не то - в коде на Rust не импортируются жирные сторонние библиотеки.

c = a + b

Тоже не то - в коде на Rust c обновляется по месту, а не создается.

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

Я в основном использую Kate, а он на Qt. Шах и мат.

Это который в KDE? Лол. Весь KDE на Qt, что тут сказать. Тут я могу только посочувствовать и пожелать всем пользователям KDE поскорей сменить сие окружение рабочего стола :-)

Нельзя, увы. При этом серьезных прог на Electron штук 5 пока.

Ну так напиши ещё одну серьёзную прогу на Electron. Будет уже 6 штук.

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

А сколько ты заплатишь? Я читал минут 10. Вроде понял что делает, но блин. a b c a.iter() ..... зипы в цепочке.... ленивые да, но..

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

Я питонщик с опытом руста около одной недели. Код, на который ты жалуешься понятен с первого прочтения, секунд 5 нужно. Аналог на питоне:

for i, (ai, bi) in enumerate(zip(a, b)):
    c[i] = ai + bi

Качай мозги, зарегься на викиуме каком-то, что ли.

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

Да, жирновастенькая она, чтоб пихать ее всюду где требуются простенькие операции с массивами.

% du -sh numpy
57M	numpy

По оперативке

user      8009 17.8  0.1 426256 27052 pts/6    Sl+  01:13   0:00 python test.py
user      8398  0.3  0.0  25000  7916 pts/6    S+   01:14   0:00 python test.py
provaton ★★★★★
()
Последнее исправление: provaton (всего исправлений: 1)
Ответ на: комментарий от MyTrooName

Я не понял до конца ли ты раскрыл свою мысль, но да, я абсолютно уверен, что оно НЕ в рантайма.

Макрос раскроется в true/false, оптимизатор выкинет ветку.

Так было в оригинале, не стал менять.

P.S. в дебаг-моде оно может действительно в рантайме :)

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

Ну, да. Кому он впал из коробки?

комменты и докстринги же чекать

(сам не пользуюсь)

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

Да ладно? Только QtGui и QtWidgets? Я ни разу не разработчик на qt, но судя по этому списочку — не только

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

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

Либо чтобы растеры могли чувствовать себя «не такими как все»

Нет, скорее всего, потому что в есть необходимость в таком синтаксисе

В такое не факт, что и плюсовый разраб захочет лезть.

Не вижу никаких граблей в коде на примерах

Лично я абсолютно не способен это читать

Это из-за соответствующих привычек и убеждений

ибо многие отбитые растеры любят фигачить в строку

Какой будет профит, если растянуть код с примеров на несколько строчек?

при этом в расте не обязательны ретурны.

1) Обязательны (если возврат не в конце выражения)
2) Как это пересекается с кодом в одну линию?
3) return смотрелся бы ущербно в замыканиях, а иметь разные правила для выражений в зависимости от контекста — было бы куда более ущербнее
4) Это не усложняет читабельность (по крайней мере, для тех, кто знает Rust на достаточном уровне)

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

в оригинале:

The is_x86_feature_detected! macro will generate code that detects if your CPU supports AVX2

так что беру свои слова обратно: таки в рантайме

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

Посмотри на Go, мало того, что он простой сам по себе, так там ещё и по стилю всё строго. За счёт этого практически любой код одинаково хорошо читается, ну и да, там рулят табы, и это прекрасно.

Я бы сказал наоборот: любой код на Go читается одинаково плохо

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

ещё в мае

2 месяца для вас это «стабильно»? или RFC - это «стабильно»? вот когда будет лет 5 без изменений - тогда стабильно. да, мёртво как xmpp, но стабильно.

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

Компилер с xmpp не совсем корректно сравнивать. Можно разрабатывать на найтли-компилере и получать рабочие програмы. А новую фичу xmpp надо ждать годами, пока популярные jabber-клиенты ее ассимилируют

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

Осиль уже хоть что-то кроме го. И ты поймешь, что НЕВОЗМОЖНО сделать етот твой явный ретурн оставив язык более-менее expression based. И не прикручивая ad-hoc правила именно для последнего выражения функции. А если там вложенное выражение?

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

можно подумать, что это лисп

Такой вот странный лисп с инфиксной записью и фигурными скобками.

tailgunner ★★★★★
()

Кароче, поцаны, вводим явный ретурн.

А потом пишем в гайдлайнах что каждая функция должна быть записана так:

fn foo() -> T {
  return {
    // code here ...
  }
}

Ура, експрешион бейсд и СИНТАКСИС ПОНЯТНЫЙ.

РЕТОРН ВИДНО.

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

В 2к18 только символы и экономить

nonvoid function foo(void) returns T {
  return (
    {
      // code here ...
    }
  );
}
makoven ★★★★★
()
Последнее исправление: makoven (всего исправлений: 2)
Ответ на: комментарий от Kuzy

оставив язык более-менее expression based

Зачем? Как у людей хипстерам совсем невмоготу?

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

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

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

Осиль уже хоть что-то кроме го

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

Тебе ретурны так глаза мозолят, или ты настолько ленив, что набор 6 символов + пробел для тебя мука адовая?

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

Да не хранение, а чтение. Кому как, конечно, но в паскале не нравилось begin/end

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

Зачем? Как у людей хипстерам совсем невмоготу?

Кстати, C-like языки считаются тоже ориентированными на выражения, только их авторы не шмогли в систему типов не из жопы и из-за этого не смогли язык сделать нормальным, пришлось в итоге костыли вроде тернарного оператора и ретёрна городить.

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

Какой будет профит, если растянуть код с примеров на несколько строчек?

Примеров?

Как это пересекается с кодом в одну линию?

Нахерачат там анонимных функций, начейнят, а часть из этого таки перенесут... глаза поломаешь это читать.

Это не усложняет читабельность

Ага, Gas тоже норм читается.

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

Самое частое применение раннего return - «отсекание» особых условий. Например в Ruby

def foo(bar)
  return if bar.nil?

  # do something with bar
end

Обычно это ненужно если в языке есть перегрузка фунцкий. В функциональном Elixir, например, вообще не существует return (возвращается только последнее выражение функции) но благодаря мощному синтаксу перегрузки функций (который поддерживает сопоставлению с образцом в сигнатуре) он не нужен. Вот например код валидации параметров который я писал где-то год назад:

  def extract(expected_type, %{"type" => expected_type, "attributes" => attributes}) do
    {:ok, attributes}
  end

  def extract(_expected_type, %{"type" => other_type}) do
    {:error, :request, status: "invalid_type", parameter: other_type}
  end

  def extract(_expected_type, _) do
    {:error, :request, status: "missing_data", parameter: "data"}
  end

В Расте полноценной перегрузки нет (ни с образцами, ни без), только с where-bound типажами, да и она неполноценная пока не допилят специализацию.

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

Или match, растовики его очень любят. Особенно когда приходится выдергивать значения из Some/None, Box, и прочих оберток.

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

Из бокса значение выдергивается с помощью *, или с помощью неявной конверсии. boxed_value.use_content()

red75prim ★★★
()
Последнее исправление: red75prim (всего исправлений: 1)

Я хочу поросить моих родителей Таню и Володю размазать по стене всех хейтеров Раста.

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

Перегрузка очень нужна, но не для этого.

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

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