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 ()
Закрыто добавление комментариев для недавно зарегистрированных пользователей (со score < 50)