LINUX.ORG.RU

Вышел Rust 1.8

 


3

7

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

Как обычно, вы можете установить Rust 1.8 с соответствующей страницы на нашем сайте и посмотреть подробные примечания к выпуску 1.8 на GitHub'е. В этом релизе было принято около 1400 патчей.

Что нового в стабильной версии 1.8

Этот выпуск содержит два нововведения, и приятный сюрприз для пользователей Windows! Помимо этого идёт работа по замене системы сборки Rust, основанной на make, на Cargo.

Первое нововведение – это возможность перегрузки составных операторов присваивания, таких как += и -=. Изменение было принято в RFC 953 и выглядит следующим образом:

use std::ops::AddAssign;

#[derive(Debug)]
struct Count { 
    value: i32,
}

impl AddAssign for Count {
    fn add_assign(&mut self, other: Count) {
        self.value += other.value;
    }
}   

fn main() {
    let mut c1 = Count { value: 1 };
    let c2 = Count { value: 5 };
    c1 += c2;
    println!("{:?}", c1);
}

Эта программа выведет Count { value: 6 }. Как и в других трейтах, перегружающих операторы, ассоциированный тип позволяет использовать разные типы в левой и правой части оператора (см. RFC 953).

Второе нововведение, взятое из RFC 218, не такое значительное. В предыдущих версиях Rust структура, не содержащая полей, должна была объявляться без фигурных скобок:

struct Foo; // works
struct Bar { } // error

Вторая форма объявления больше не является ошибочной. Изначально эта форма была запрещена из соображений согласованности с другими пустыми объявлениями, а также для предотвращения неоднозначности синтаксического разбора. Но эта неоднозначность была устранена, начиная с Rust 1.0. Кроме того, запрет этой формы создавал трудности при написании макросов, требуя специальной обработки. Наконец, пользователям, ведущим активную разработку, иногда требовалось менять пустую структуру на непустую и наоборот, что требовало лишней работы и приводило к некрасивым diff'ам.

Возвращаясь к Windows — теперь 32-х битные MSVC сборки поддерживают размотку стека, что переводит платформу i686-pc-windows-msvc в класс 1 (о классах поддержки платформ).

Мы с давних пор используем make для сборки Rust'а, но у нас уже есть своё замечательное средство сборки для программ на Rust: Cargo. В Rust 1.8 мы добавили предварительную поддержку новой системы сборки, написанной на Rust и основанной на Cargo. Мы ещё не используем её по умолчанию, и она требует значительной доработки, поэтому подробное её описание появится в примечаниях к выпуску после её завершения. Сейчас вы можете посмотреть подробности по ссылке на PR 31123.

Стабилизация библиотек

В Rust 1.8 около 20 функций и методов было переведено в категорию стабильных. Их можно разбить на три группы: работа со строками в кодировке UTF-16, различные API для работы со временем, и дополнительные трейты, необходимые для перегрузки операторов, упомянутые в секции об изменениях в языке.

Нововведения в Cargo

  • cargo init создаёт проект в текущем каталоге, не создавая новый, как делает cargo new
  • cargo metadata - дополнительная субкоманда для получения метаданных
  • .cargo/config теперь допускает ключи -v и --color
  • Улучшены возможности Cargo по поддержке платформоспецифичных зависимостей.

Подробное описание изменений.

>>> Вышел Rust 1.8

★★★

Проверено: maxcom ()
Последнее исправление: shahid (всего исправлений: 5)
Ответ на: комментарий от Xroft

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

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

синтаксис для языка программирования вещь второстепенная

Если от него слезятся глаза и хочется блевать, то таки первостепенная 😂 Ведь над кодом придётся работать несколько часов в день.

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

Ну у раста всё-таки не так всё запущено, ИМХО. Непривычно после крестов, но это скорее вопрос привычки, чем что-то объективное (хотя лямбдам в стиле Ruby в любом случае трудно найти оправдание).

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

Синтаксис у него просто вырвиглазный.

Дело привычки.

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

Синтаксис конечно.
Избранные теги: clos, common lisp, emacs, emacs lisp, erlang, lisp, lispworks, machine learning, ml, prolog, sbcl

Спасибо, повеселил.

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

(ну или на худой конец как в восьмой джаве)

Существенной разницы не вижу. Разные «скобки» для параметров, обязательность -> в джаве и невозможность указать возвращаемый тип. Пожалуй, вариант раста мне нравится даже немного больше.

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

пример гипотетического синтаксиса Rust (с генериками, лайфтаймами, лямбдами, атрибутами и прочим).

В чём проблема записывать свойства символов в виде plist?

(defvar (i :type int) 42)

(setf x (deref someptr))

foo::<i32>(...) -> ((foo :spec i32) ...)

И т.п. Да писать чуть больше, зато всё понятно и не надо ломать глаза об эти ваши &::->*

no-such-file ★★★★★
()
Ответ на: комментарий от no-such-file

В чём проблема записывать свойства символов в виде plist?

Ну, на однострочниках легко демонстрировать превосходство синтаксиса с блекджеком и plist'ами. Но ты мне лучше покажи, как будет выглядеть на Lispy Rust следующий фрагмент, а то я сам попробовал, и мне результат не понравился.

#[must_use = "futures are lazy and do nothing unless consumed"]
pub struct Future<T: Send + 'static, E: Send + 'static> {
    pub core: Option<Core<T, E>>,
}

impl<T: Send + 'static, E: Send + 'static> Future<Option<(T, Stream<T, E>)>, E> {
    /// An adapter that converts any future into a one-value stream
    pub fn to_stream(mut self) -> Stream<T, E> {
        stream::from_core(core::take(&mut self.core))
    }
}

impl<T: Send + 'static, E: Send + 'static> Async for Future<T, E> {
    type Value = T;
    type Error = E;
    type Cancel = Receipt<Future<T, E>>;

    fn is_ready(&self) -> bool {
        core::get(&self.core).consumer_is_ready()
    }
}

И это, прошу заметить, далеко не самый навороченный (в плане сахара и синтаксиса) код на Rust.

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

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

Как будто я понимаю этот ваш rust... Ну давай пофантазируем, например первый кусок:

(struct (Future :access pub 
                :template (T (+ Send (' static))
                           E (+ Send (' static))
        (core :access pub 
              :value (Option :template (Core: template (T E)))))

Дальше мне просто лениво, но в чём проблема построить AST, ведь оно всё равно строиться компилятором? Хотя это кончено не то что надо, т.к. вообще говоря шаблоны в лисп-синтаксисе просто не нужны, т.к. есть макросы. Если говорить об аналоге rust с синтаксисом лиспа, то выглядеть это будет не то что не 1:1, а совершенно иначе. Например повтор

<T: Send + 'static, E: Send + 'static>
просто режет глаз - в лиспе такие вещи просто делаются отдельной сущностью.

no-such-file ★★★★★
()
Ответ на: комментарий от no-such-file

т.к. вообще говоря шаблоны в лисп-синтаксисе просто не нужны, т.к. есть макросы

В расте есть и шаблоны (дженерики) и макросы.

просто режет глаз - в лиспе такие вещи просто делаются отдельной сущностью.

Какой/как?

DarkEld3r ★★★★★
()
Ответ на: комментарий от no-such-file

Дальше мне просто лениво, но в чём проблема построить AST, ведь оно всё равно строиться компилятором?

Я не говорил, что исходники на Rust нельзя представить в виде sexp, я просто усомнился, что от этого повысится читаемость. Ну я вот не поленился, и перевёл пример на твой синтаксис (я взял на себя смелость добавить в некоторых местах скобок, чтобы избежать неоднозначностей), но получилось говно:

((struct :annotation (must-use "futures are lazy and do nothing unless consumed"))
 (Future :access pub
         :template ((T (+ Send (' static)))
                    (E (+ Send (' static)))))
 (core :access pub
       :value (Option :template ((Core :template (T E))))))

(impl :template ((T (+ Send (' static)))
                 (E (+ Send (' static))))
      (Future :template ((Option :template ((Tuple :template (T (Stream :template (T E))))))
                         E))
      (defun (to-stream :access pub) ((self :modifier ('mut)) :return (Stream :template (T E)))
        (stream:from-core (core-take (deref-mut (field self 'core))))))

(impl :template ((T (+ Send (' static)))
                 (E (+ Send (' static))))
      :trait Async
      (Future :template (T E))
      :assoc-types ((Value T)
                    (Error E)
                    (Cancel (Receipt :template ((Future :template (T E))))))
      (defun is-ready ((self :modifier ('ref)) :return bool)
        ;; Либо clojure-way: (.consumer-is-ready? (core:get (field (ref self) 'core))
        (call-instance-method (core:get (field (ref self) 'core))
                              'consumer-is-ready?)))

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

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

Вот с этого места поподробнее. Как макросы могут заменить дженерики?

<T: Send + 'static, E: Send + 'static>

просто режет глаз - в лиспе такие вещи просто делаются отдельной сущностью.

Ну и как подобные вещи делаются в лиспе? Поясню, этот фрагмент означает, что у типа есть два обобщенных параметра T, U, каждый из которых должен реализовывать трейт Send и иметь статическую продолжительность жизни.

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

Какой/как?

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

no-such-file ★★★★★
()
Ответ на: комментарий от theNamelessOne

Код получился абсолютно нечитаемым

Потому что получилась тонна копипасты которую нужно факторизовать завернув в макросы.

Как макросы могут заменить дженерики?

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

есть два обобщенных параметра T, U, каждый из которых должен реализовывать трейт Send и иметь статическую продолжительность жизни

Макрос может на этапе компиляции проверять, подходят ли переданные параметры под нужный тип, раз уж тип задаётся в воображаемом псевдорастолиспе с помощью plist.

no-such-file ★★★★★
()
Ответ на: комментарий от no-such-file

Код получился абсолютно нечитаемым


Потому что получилась тонна копипасты которую нужно факторизовать завернув в макросы.

Т.е. от первоначального утверждения ¨синтаксис на sexp более читаемый¨ мы перешли к утверждению ¨синтаксис на sexp более читаемый, если скрыть тонны boilerplate за макросами¨? Ну хорошо, факторизируй копипасту отсюда, завернув её в макросы.

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

Чёт тебя куда-то понесло. Перефразирую: как можно макросами заменить дженерики, оставаясь в рамках системы типов и семантики Rust? Примеров мне, примеров!

Макрос может на этапе компиляции проверять, подходят ли переданные параметры под нужный тип, раз уж тип задаётся в воображаемом псевдорастолиспе с помощью plist.

Т.е. ты хочешь накостылить дженерики на макросах? Но зачем? И да, без указания type constraint'ов это сделать не получится, потому что у тебя не будет нужной информации.

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