LINUX.ORG.RU

Релиз языка программирования Rust 1.39

 ,


1

8

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

Что нового в версии 1.39:

  • стабилизирован новый синтаксис асинхронного программирования, основанный на функции «async», блоке async move { … } и операторе «.await»;
  • разрешено указание атрибутов при определении параметров функций, замыканий и указателей на функции. Поддерживаются атрибуты условной компиляции (cfg, cfg_attr), управляющие диагностикой через lint и вспомогательные атрибуты вызова макросов;
  • стабилизирован «#feature(bind_by_move_pattern_guards)», который позволяет использовать переменные с типом привязки «by-move» в шаблонах;
  • предупреждения о проблемах при проверке заимствования переменных c использованием NLL переведены в разряд фатальных ошибок;
  • в пакетный менеджер cargo добавлена возможность использования расширения «.toml» для файлов конфигурации.

С полным списком изменений можно ознакомиться на сайте разработчика.

>>> Источник

★★★★★

Проверено: cetjs2 ()

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

Это все разговоры на лавочке. C примитивен. В Rust нужно еще вызубрить lifetime, generics, traits, macros.

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

Приведу пример из личного опыта. Старшекурсником устроился в софтовую контору. Платили мало, так что не говорю, что там были сливки общества, но тем не менее не одни студенты там работали. И был там несколько чуваков на правах сеньёров. Один из них, дядя за 25, писал прослойку работы с данными, фактически просто кэшировал базу и выдавал массивы чисел по запросу. Не сказать, что чувак был совсем не любопытным, помню как он спорил с начальником на тему зануления указателей, типа он вычитал в книге трупа страуса о том, что правильные пацаны не пишут NULL, а используют 0, компилятор знает как преобразовать литерал в указатель правильного типа. Т.е. чувак не балду пинал, а занимался самообразованием. Так вот, помню слышал, как этот чувак коллеге объяснял изменение API, говорил, что выданные его подсистемой объекты нужно удалять. Простым delete. Но не всегда: при получении массива ты мог получить как есть, а мог попросить преобразованный, ну там флоаты в даблы перевести или подмассив выдать. Так вот, преобразованные надо удалять, а вариант «как есть» удалять не надо, потому что они шарились. Простая идея, что если твоя функция выделяет память, то ты же и должен написать функцию по освобождению, в которой сам и проверишь, надо ли массив удалять ему в голову не пришла. Т.е. годам к 30, может, устав бороться с косяками доступа к память, он бы и догадался, а может и нет, просто обернул бы в shared_ptr и косяки бы ещё глубже под ковёр заметены были б.

Отсюда мораль: мало изучить C/C++ за 21 день, надо ещё 21 день потратить на изучение неформальных правил ручного управления ресурсами, вместе 42 дня получается. Так не лучше ли потратить эти 42 дня на rust? Тогда и на лайфтаймы, и на дженерики с макросами время хватит, и получишь инструмент для автоматического контроля лайфтаймов.

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

И, внезапно, они совпадают с некими «бест практикс», которые позволят программе не начать падать после небольшого усложнения в совершенно другом месте.

Тут согласен. Но это заслоняет суть работы железа и ОС.

То есть, для профессионалов он очень подходит, но вот для понимания программирования как такогового - далеко не очень.

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

Кхм. В контексте сравнения растофичей и современного C++ – мне и не надо понимать как оно работает в LLVM: в расте то же самое существует полностью статически, и без модификации кода.

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

А можно пример, того что существует в расте полностью статически и чего нет в С++, а то никто так и не может привести?

Конкретный код, который с багой и на который ругается раст, и на который не будет ругаться С++ (со всеми включенными варнингами)

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

Я тебе даже лучше дам пример: в цпп нельзя передать unique_ptr в лямбду, вызывающуюся один раз. В расте можно, т.к. там есть для таких вещей отдельный тип FnOnce.

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

а то никто так и не может привести

Вы просто не туда смотрите.

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

Я тебе даже лучше дам пример: в цпп нельзя передать unique_ptr в лямбду, вызывающуюся один раз. В расте можно, т.к. там есть для таких вещей отдельный тип FnOnce.

Спасибо, на самом деле можно, это частое заблуждение. Думают что в С++ в лямбду можно передавать лишь по значению, по ссылке и по мутабельной ссылке. Но можно передавать и по move. Вот синтаксис. Но на этот код не ругнулся MSVC компилятор:( Нужно будет ещё проверить в clang и clang-tidy.

#include <iostream>
#include <memory>
int main() {
	std::unique_ptr<int> p = std::make_unique<int>(92);
	auto fn_once = [p = std::move(p)]() mutable {
		std::cout << *p << std::endl;
		p.release();
	};
        // std::cout << *p << std::endl; // вот на эту строчку ругается компилятор, а на двойной fn_once нет :(
	fn_once();
	fn_once();
}

а на такой код на расте компилятор ругнулся:

fn main() {
    let p = Box::new(92);
    let fn_once = | | {
        println!("{}", *p);
        drop(p);
    };
    fn_once();
    fn_once();
}
error[E0382]: use of moved value: `fn_once`
 --> src\main.rs:8:5
  |
7 |     fn_once();
  |     ------- value moved here
8 |     fn_once();
  |     ^^^^^^^ value used here after move
  |
note: closure cannot be invoked more than once because it moves the variable `p` out of its environment
 --> src\main.rs:5:14
  |
5 |         drop(p);
  |   

Я на самом деле искал примеры что может делать раст и чего не может делать С++. Например вот в этом плейлисте примеры из первых 6 лекций все ложные: https://www.youtube.com/watch?v=Oy_VYovfWyo&list=PLlb7e2G7aSpTfhiECYNI2EZ1uAluUqE_e Там показываются примеры когда в С++ UB, а на расте ошибки, но у меня и на С++ были ошибки, например на таком коде современный компилятор ругается, как и в rust:

#include <vector>
#include <iostream>
int main() {
    std::vector<int> xs = { 1,2,3,4,5 };
    for (auto x : xs) {
        std::cout << x << std::endl;
        xs.push_back(x);
    }
}

Зарепорчу баг в багзилу MSVC с примером fn_once…

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

на таком коде современный компилятор ругается

Почему вы продолжаете сравнивать hello world’ы? Вы же понимаете, что вы сравниваете худо-бедно работающие метрики с компилятором, который проводит полный анализ кода? Выше уже был пример с clang-костылём для проверки верно проставленных мютексов. В Rust - это неотъемлемая часть языка. Её не победить.

Пока даже банальный use-after-move в плюсах - не ошибка, ловить там нечего.

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

Да в стандарте это пока не ошибки. Но у раста вообще нет стандарта.

А на уровне компилятора есть такой pdf: https://github.com/isocpp/CppCoreGuidelines/blob/master/docs/Lifetime.pdf и заявление Microsoft что они реализовали этот pdf в своём компиляторе.

Зарепортил баг: https://developercommunity.visualstudio.com/content/problem/827037/c26489c26488-false-negative.html

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

Да в стандарте это пока не ошибки. Но у раста вообще нет стандарта.

Аргумент уровня C++.

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

Спасибо, на самом деле можно, это частое заблуждение.

Да, так можно. Но обычно замыкание куда-то передают, и тут ждёт облом:

#include <iostream>
#include <memory>
#include <functional>
void exec(const std::function<void()> &f) {
  f();
}
int main() {
  std::unique_ptr<int> p = std::make_unique<int>(92);
  auto fn_once = [p = std::move(p)]() mutable {
    std::cout << *p << std::endl;
    p.release();
  };
  exec(fn_once);
}
unC0Rr ★★★★★ ()
Ответ на: комментарий от unC0Rr

В С++ много чего есть, например std::reference_wrapper

#include <iostream>
#include <memory>
#include <functional>
void exec(const std::function<void()> &f) {
  f();
}
int main() {
  std::unique_ptr<int> p = std::make_unique<int>(92);
  auto fn_once = [p = std::move(p)]() mutable {
    std::cout << *p << std::endl;
    p.release();
  };
  exec(std::reference_wrapper(fn_once));
}
fsb4000 ★★★ ()
Ответ на: комментарий от fsb4000

Ага! Вот чего мне не хватало, огромное спасибо. В этом раст выигрывает, что там не нужно подпирать код при помощи таких штук.

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

А есть возможность для такого?

exec([p = std::move(p)]() mutable {
    std::cout << *p << std::endl;
    p.release();
  });
unC0Rr ★★★★★ ()
Ответ на: комментарий от unC0Rr

Да, так как reference_wrapper нужно чтобы наша лямбда жила, он не всегда подходит…

Вот что нашёл в интернете:

#include <iostream>
#include <functional>
#include <memory>
#include <utility>
#include <future>
#include <iostream>
#include <type_traits>
#include <cstddef>

template <typename F>
class shared_function : public std::shared_ptr<F> {
public:
	using std::shared_ptr<F>::shared_ptr;

	template <typename ...Args>
	auto operator()(Args&&...args) const
		-> typename std::result_of<F(Args...)>::type
	{
		return (*(this->get()))(std::forward<Args>(args)...);
	}
};

template <typename F>
shared_function<F> make_shared_fn(F&& f)
{
    return shared_function<F>{
		std::make_unique<typename std::remove_reference<F>::type>( std::forward<F>(f))
	};
}

std::function<void()> test() 
{
    std::unique_ptr<int> p = std::make_unique<int>(92);
    auto fn_once = [p = std::move(p)]() mutable {
        std::cout << *p << std::endl;
        p.release();
    };
    return make_shared_fn(std::move(fn_once));
}

void exec(const std::function<void()>& f) {
    f();
}
int main() 
{
    exec(test());
    std::unique_ptr<int> p = std::make_unique<int>(92);
    exec(make_shared_fn([p = std::move(p)]() mutable {
		std::cout << *p << std::endl;
		p.release();
	}));
}

Я не до конца ещё понимаю make_shared_fn, но она владеет лямбдой, и теперь нам не важно время жизни оригинальной лямбды в отличие от std::reference_wrapper

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

Любопытное решение, но выглядит уже оверкиллом. Проще тогда уж захватываемый unique_ptr конвертировать в shared_ptr, чем саму лямбду засовывать в кастомный shared_ptr с оператором ().

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

Да, но это всё когда именно нужно std::function.

Если просто вызывать, то работают простые шаблоны

template<class F>
void exec2(F&& f) {
    f();
}

int main() {
    std::unique_ptr<int> q = std::make_unique<int>(92);
    std::unique_ptr<int> h = std::make_unique<int>(92);

    auto r = [p = std::move(q)]() mutable {
        std::cout << *p << std::endl;
        p.reset();
    };
    exec2(std::move(r));
    exec2([p = std::move(h)]() mutable {
        std::cout << *p << std::endl;
        p.reset();
    });
}
fsb4000 ★★★ ()
Ответ на: комментарий от fsb4000

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

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

Если эти лямбды будут вызываться ровно один раз, то можно использовать https://en.cppreference.com/w/cpp/thread/packaged_task

packaged_task достаточно лишь move конструктора, в отличие от std::function и тоже можно положить в контейнер…

https://godbolt.org/z/CaHGsw

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

Гмм. Странно, я на gcc 7.4 получаю такое, хотя на годболте нормально всё:

terminate called after throwing an instance of 'std::system_error'
  what():  Unknown error -1
C clang-ом эта программа работает, но аналогичное решение в большой программе падает с
terminate called after throwing an instance of 'std::future_error'
  what():  std::future_error: No associated state

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

Сдаётся мне, где-то тут UB зарыто, потому может и работать иногда.

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

Попробовал ошибки поделать, если два раза вызвать функцию, то ошибка

terminate called after throwing an instance of 'std::future_error'
  what():  std::future_error: Promise already satisfied

https://godbolt.org/z/BJHX_k

если создавать пустой packaged_task, то ошибка:

terminate called after throwing an instance of 'std::future_error'
  what():  std::future_error: No associated state

https://godbolt.org/z/JPVAgf

https://godbolt.org/z/6sTDH4

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

Да, нашёл в чём дело, у меня очередь может иногда модифицироваться во время итерирования по ней. Раньше работало прозрачно, потому что я другие контейнеры и по-другому использовал. Но вопрос с крешем тестового решения остаётся открытым.

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

Но вопрос с крешем тестового решения остаётся открытым.

Может в том старом gcc баг… Этот код на студии вообще не собирается, но они ответили что знают о нём, но фикс требует изменения ABI, так что будет лишь в следующей студии. (с 2015 по 2019 студий ABI у MS не менялся)

https://github.com/microsoft/STL/issues/321

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

В общем, вся эта дискуссия любопытна в рамках обсуждения раста. С одной стороны, в расте нет такой проблемы с unique_ptr, с другой я бы не вляпался в изменение контейнера во время итерирования, но с третьей стороны - я хз как писать очередь тасков в нём. Пришлось бы протаскивать мутабельную ссылку на очередь всюду, где может потребоваться?

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

Мутабельная ссылка на очередь может быть только одна (её можно рассматривать как compile time write lock), так что протащить её не получится. Нужно будет доступаться к очереди через какой-то механизм (межпоточной) синхронизации. Как организовать доступ к этому механизму? Как угодно. Положить в глобальную переменную (lazy_static), положить в thread local, прокидывать в нужные функции.

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

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

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

Под «протащить» я имею в виду передавать в каждую функцию, где она может потребоваться, это вполне возможно в расте. Невозможно сохранить её, но это другое.

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

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

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

Но у раста вообще нет стандарта.

В смысле ISO? А у кого он вообще есть? Я сходу назову только три: C, С++ и Fortran. Все из них уже закопали по несколько раз.

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

Ух ты! Не знал, что у шарпа есть ISO’шный стандарт. Ну что же, тем лучше для шарпа.

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

Нет, школа закончилась уже как 11 лет назад ) А злой потому как Perl и JS реально ущербные языки

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

Ну-ка, расскажите мне об этом. Что не так с Perl и JS? Можно даже тредик в Talks создать. С удовольствием почитаю.

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

Perl

  1. Ну для начала стоит заказать что perl скорее мертв чем жив.

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

  3. Добиться параллельности не отстрелив себе ноги через форки процессов невозможно

  4. Отсутствия асинхронности

  5. Обратная совместимость между версиями всегда находится в печальном состоянии

  6. Практически полностью вымерла комьюнити

Nodejs

  1. Написан за пару дней, из-за чего огромные проблемы в структуре

  2. Огромные проблемы с математикой, неточные расчеты при работе с плавающей точки

  3. https://habr.com/post/337098/ - Сам создатель Ноды сказал что создал монстра, абсолютно негодного для веб бэка

  4. Прожорливый

  5. npm - одно из самых паршивых пакетных менеджеров и одно из самых небезопасных

  6. Комьюнити которое закапывает поддержку своих продуктов, очень частое явление

  7. Хотите использовать многоядерность -> плоди процессы(worker’s пока не вошли в стандарт так что их пока не беру в расчет)

И очень очень много другого.

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