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)

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

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

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

Я заодно за обработку ошибок. И красивее получается.

fn get_port(path: &Path) -> Result<u64, Error> {
    let json: Value = from_str(&fs::read_to_string(path)?)?;
    Ok(json["server"]["port"].as_u64()?)
}
red75prim ★★★
()
Ответ на: комментарий от RazrFalcon

Не-не-не, Дэвад Блейн.
Ты говорил про синтаксис, который «может быть проще».
Чем a=( 1 2 3 ) сложнее a=[ 1, 2, 3]?
А echo ${a[1]} сложнее print(a[1])? С учётом того, что «К экранированию строк и переменных у меня вопросов нет.»(цы)

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

Чем echo ${a[1]} сложнее print(a[1])?

тем, что в первом случае это echo и ${a[1]}, а во втором - print() и a[1], очевидно же

и еще ты заставил цикл for исчезнуть

MyTrooName ★★★★★
()
Последнее исправление: MyTrooName (всего исправлений: 1)
Ответ на: комментарий от MyTrooName
let port = 
    json["server"]["port"]
    .as_u64()
    .ok_or_else(|| err_msg("`server.port` doesn't exist or cannot be represented as `u64`"))?;
Ok(port)
red75prim ★★★
()
Последнее исправление: red75prim (всего исправлений: 1)

Rust
ориентированный на безопасность, скорость и параллельность.

Подчеркните любые два.

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

А что тебе в том цикле?
Обычный for in.
${a[@]} обозначает все записи в массиве.
Взятие в кавычки - экранирование пробелов, чтобы "a b" не превратилось в "a" "b"
Хотя в том случае это и не обязательно.
Или у тебя претензии к for i in ...; do :; done; ?

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

find . -atime +30 | xargs rm

ага, особенно директория (чтобы пораньше выдалась) "-r" + файл: "... /полезная-директория"

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

И красивее получается.
)?)?;

Да, прикольные смайлики. Пеши исчо.

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

Моя претензия к количеству синтаксического мусора.

a=( 1 2 3 )
echo ${a[1]}
for i in "${a[@]}"; do
    echo ${i}
done

vs

a = [ 1, 2, 3 ]
print(a[1])
for v in a:
    print(v)

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

Т.е. синтаксис 'for' ты не в курсе?

Всё ясно - никаких обоснованных претензий нету, но прикопаться хочется.

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

ориентированный на безопасность, скорость, параллельность и дружелюбность

вот так надо

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

Моя претензия к количеству синтаксического мусора

К экранированию строк и переменных у меня вопросов нет

Ты уж определись.

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

Всё ясно - никаких обоснованных претензий нету, но прикопаться хочется.

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

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

Не шлангуй, он не про экранирование. Но, конечно, без шлангования трудно говнобаш в тредах про нормальные ЯП защищать.

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

Нет. Запишем то, что ты дурак и не знаешь предмет спора.

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

в bash синтаксис шагает от командно-строчной природы - понимать надо. он никогда не задумывался как полноценный ЯП

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

Еще и изворачивались, чтоб сохранить обратную совместимость с sh. Прям как VimL — уродливый наследник командного режима vi.

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

Ага, viml страшный почти как раст. Но как видишь для всех старых уродцев есть исторические причины быть такими. И только раст вырвиглазен просто так, без особых причин.

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

ЯННП, но у меня в rm точно нет опции -r.

touch 'dir ' ./' -r'; find . | xargs printf '%s\n' — а rm может удалить что-нибудь левое (выше у меня криво, но не принципиально)

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

А опыт собственно работы с языком у вас был?

у eao на блогспоте есть статья с примерами, как в расте можно насрать вебе в шаровару всего за одну букву.

http://eao197.blogspot.com/2018/06/progflame-rust-127.html

помоему это - атас.

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

у eao на блогспоте есть статья с примерами, как в расте можно насрать вебе в шаровару всего за одну букву.

Очень доходчивое описание проблемы.

tailgunner ★★★★★
()
Ответ на: комментарий от i36_zubov
fn new<'a: 'f>(foo: &'f Foo<'a>) -> Self {
    let foo1: &'f Foo<'f> = foo;
    Context { foo: foo1 }
}

Божественно! Перл с башем нервно курят в коридоре.

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

Но в этот раз хочется заострить внимание на другом. Всегда, когда доводилось погружаться в Rust, ловил себя на том, что при чтении Rust-овского кода приходится заострять внимание буквально на каждом символе. И вот наткнулся на очередное подтверждение этого впечатления. Простая программка:

fn main() {
    let x = 1;
    let y = {
        x+1
    };
    println!("x={:?}, y={:?}", x, y);
}
Результат работы вполне ожидаемый: x=1, y=2

Добавляем всего одну точку с запятой:

fn main() {
    let x = 1;
    let y = {
        x+1;
    };
    println!("x={:?}, y={:?}", x, y);
}
И получаем уже принципиально другой результат: x=1, y=()

Это потому, что блок кода — это выражение (expression). И если последняя строка кода в таком выражении не имеет завершающей точки с запятой, то именно эта строка (т.е. выражение в этой строке) будет определять тип и результат всего блока кода. А вот если последняя строка завершается точкой запятой, то у всего блока кода типом и результатом будет специальный пустой тупл (или пустая структура, что в случае Rust-а, как я понял монопенисуально).

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

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

Код, по типам эквивалентный let y = (), достоен варнинга даже на дефолтном уровне предупреждений. Зачем кому-то записывать пустой кортеж в переменную?

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

Код, по типам эквивалентный let y = (), достоен варнинга даже на дефолтном уровне предупреждений

Да? И в каком языке так делается?

Зачем кому-то записывать пустой кортеж в переменную?

А предупреждения надо выдавать на все кнонструкции, которым ты не можешь придумать смысла?

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

И в каком языке так делается?

Серьёзно? Это твой аргумент?
Что-то уровня «если Петя с десятого этажа прыгнет тогда и я прыгну».

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

И в каком языке так делается?

Что-то уровня «если Петя с десятого этажа прыгнет тогда и я прыгну».

Серьезно? Это твоя логика?

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

Да? И в каком языке так делается?

Во всех языках, что лично я знаю, эти грабли от eao197 невозможны в принципе. Если ты можешь привести пример таких языков (и там тоже не будет варнинга, как в раст) — велком.

Зачем кому-то записывать пустой кортеж в переменную?

А предупреждения надо выдавать на все кнонструкции, которым ты не можешь придумать смысла?

Так может быть ты поведаешь сакральный смысл этой конструкции? :)

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

И только раст вырвиглазен просто так, без особых причин.

Нужно прокачать IQ немножко и всё станет понятно.

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

Как нужно упороться, чтобы в y = { x+1; } увидеть очевидный NULL? Отсыпь, я тоже хочу видеть сквозь стены.

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

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

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

И в каком языке так делается?

Что-то уровня «если Петя с десятого этажа прыгнет тогда и я прыгну».

Серьезно? Это твоя логика?

нет, это твоя логика.

(пожимая плечами) Окей.

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