LINUX.ORG.RU

Rust 1.18

 


1

10

Команда Rust анонсирует релиз 1.18.

Обновление предыдущей версии легко:

$ rustup update stable

Сам rustup можно установить здесь.

Основные изменения

Одно из главных изменений - новая версия «The Rust Programming Language», официального учебника по Rust. Он пишется открыто на Github, и имеет более ста авторов. В этом релизе включен черновик второй версии книги, имеющий 19 из 20 глав; двадцатая глава будет готова к релизу 1.19. Купить бумажную версию можно через No Starch Press. Новая версия книги полностью переписана и учитывает последние два года нашего опыта обучения Rust. Вы найдете новые объяснения основных принципов Rust, новые проекты и прочее.

В самом языке улучшено ключевое слово pub. По умолчанию, в Rust объекты приватны; можно использовать pub чтобы сделать их публичными. В Rust 1.18, pub имеет новый вариант:

pub(crate) bar;

Слово в скобках - ограничение, контролирующее степень публичности объекта. Если указанно pub(crate), то bar будет публичным для всего крейта (пакета), но не вне него. Это позволяет декларировать интерфейсы, «внутренне публичные» для пакета, но не доступные для внешних пользователей.

Также можно указать путь, например:

pub(in a::b::c) foo;

Это значит «доступно в иерархии a::b::c, но не в прочих местах».

Для пользователей Windows Rust 1.18 имеет новый атрибут, #![windows_subsystem]. Он работает так:

#![windows_subsystem(console)]
#![windows_subsystem(windows)]

Он контролирует флаг /SUBSYSTEM в компоновщике. На текущий момент доступны только console и windows. Если вы разрабатываете графическое приложение, и не указываете windows, в момент пуска программы всплывет окно консоли. С атрибутом windows этого не произойдет.

Далее, в Rust кортежи, варианты перечисляемых типов и структуры (без атрибута #[repr]) всегда имели неопределенное расположение в памяти. Мы включили автоматическое упорядочивание, которое может привести к уменьшению потребления памяти путем уменьшения необходимого выравнивания. Например:

struct Suboptimal(u8, u16, u8);

В прежних версиях Rust на платформе x86_64 эта структура имела бы размер в шесть байтов. Но согласно исходному коду, ей достаточно должно быть четырех. Остальные два байта - результат выравнивания. Поскольку мы имеем u16, он требует двух байтов. Но в данном случае, он был смещен на один байт из-за предыдущего u8. Для последнего же u8 требуется еще один байт выравнивая. В итоге, мы имеем 1 + 1 (пусто) + 2 + 1 + 1 (пусто) = 6 байтов.

Но что если структура выглядит так?

struct Optimal(u8, u8, u16);

Эта структура оптимально выравнена; u16 находится на рубеже двух байтов, как и остальная структура. Выравнивание не требуется. Это дает нам 1 + 1 + 2 = 4 байта.

При дизайне Rust мы оставили физическое расположение данных в памяти неопределенным как-раз по этой причине; любой safe-код (не следующий по «сырым» указателям) не будет затронут подобной оптимизацией. Благодаря этому, мы можем научить компилятор оптимизировать Suboptimal в Optimal автоматически. В Rust 1.18 обе структуры занимают в памяти размер в четыре байта.

Мы долго планировали это изменение; оно было и ранее в нестабильной версии Rust, но некоторые программисты писали unsafe-код, который предполагал определенное расположение данных в памяти. Если вам необходима гарантия, что физическое расположение в памяти в точности совпадает с расположением вариантов в исходном коде (например, при обращению к оболочкам Cи-кода), пометьте вашу структуру с атрибутом #[repr(C)].

Напоследок, улучшено время компиляции; например, компиляция самого rustc теперь на 15%-20% быстрее.

Стабилизированы следующие библиотеки:

  • Child::try_wait, неблокирующая форма Child::wait.
  • HashMap::retain и HashSet::retain - версия существующего retain от Vec<T> теперь и у этих двух структур.
  • PeekMut::pop позволяет взять ранее прочитанный верхний элемент от BinaryHeap<T> без необходимости повторно упорядочивать кучу.
  • TcpStream::peek, UdpSocket::peek, UdpSocket::peek_from позволяют прочесть крайний элемент у потока или сокета.

Новый функционал Cargo

Cargo добавил поддержку системы управления версиями Pijul, который написан на Rust:

cargo new my-awesome-project --vcs=pijul

У Cargo несколько новых флагов, дополняющих --all: --bins, --examples, --tests и --benches позволяют собрать все программы указанных типов.

И наконец, Cargo теперь поддерживает Haiku и Android.

Подробнее об изменениях написано здесь.

>>> Подробности



Проверено: Shaman007 ()
Последнее исправление: cetjs2 (всего исправлений: 7)

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

Есть три вида лжи - ложь, гнусная ложь, и бенчмарки. :-) Но что нашел, то и выдаю.

Лично я считаю, что у Раста максимальные шансы занять часть ниши Си или Плюсов в веб-сфере, где нынче всех пугают всякие Heartbleed-ы и прочие уязвимости, которые Rust ставит решать как одну из основных целей. Если не сервер-инфрактруктур, то всяких утилит вроде openssl - вполне. Причем, по сравнению с теми же Плюсами, на нем (в теории) должно быть намного проще писать безопасный параллельный код.

Он не достигнет популярности Node или Go в сферах, где они сильны (вер-фрейморки итп.), но это не значит, что он хуже в других сферах, или вообще не может играть никакой роли на вебе.

И да, теория - не практика. Цыплят по осени считают.

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

Да, это был эксперимент. Асинхронная библиотека Tokio до сих пор в бете. Вполне возможно, что новый проект ко-рутин ее заменит вообще.

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

Понятно. Есть подозрение, что разрыв в 25% может быть вызван не столько скоростью Rust-а, сколько качеством и функциональностью парсера minihttp в сравнении с fasthttp. Но все равно очень интересно.

Лично я считаю, что у Раста максимальные шансы занять часть ниши Си или Плюсов в веб-сфере, где нынче всех пугают всякие Heartbleed-ы и прочие уязвимости, которые Rust ставит решать как одну из основных целей.

Тут скорее, замена C. Плюсов там, насколько я знаю, исторически не так много.

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

медленее писать код

Костыли для генерации дженериков и простыни с ручной обработкой ошибок конечно же ускоряют написание кода.

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

Тут скорее, замена C. Плюсов там, насколько я знаю, исторически не так много.

По-этому я и предположил. С - простой язык по сравнению с С++, но имеет намного меньше инструментов - не только ООП, но и мощная типизация, шаблонирование, итп. С точки зрения экспрессивности и инструментария самого языка (не путать с количеством существующих библиотек), Раст ближе к С++ чем к С (типажи, дженерики, макросы, строгая типизация, итп). Плюс нынче модные конструкты вроде pattern-matching. О безопасности памяти и строгом отношении к null и исключениям, известно и так.

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

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

Ну а что касается производительности, то по тем же микробенчмаркам, Раст немного уступает Си, но с Плюсами бодается на равных. Где-то получше, где-то похуже, но в целом примерно одинакого.

Памяти жрет значительно больше, это да. Go - и тот ест меньше.

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

Понятно. Есть подозрение, что разрыв в 25% может быть вызван не столько скоростью Rust-а, сколько качеством и функциональностью парсера minihttp в сравнении с fasthttp. Но все равно очень интересно.

Я вот как раз обратил внимание на то же качество обработки ошибок. Да и общая понятность впечатляет, конечно:

impl<'req> Iterator for RequestHeaders<'req> {
    type Item = (&'req str, &'req [u8]);

    fn next(&mut self) -> Option<(&'req str, &'req [u8])> {
        self.headers.next().map(|&(ref a, ref b)| {
            let a = self.req.slice(a);
            let b = self.req.slice(b);
            (str::from_utf8(a).unwrap(), b)
        })
    }
}
asaw ★★★★★
()
Ответ на: комментарий от pftBest

Спасибо за ссылку.

Но, блин, ну как не сказать про читабельность кода:

fn start_server<P: AsRef<Path>>(addr: &str, path: P) {
    let addr = addr.parse().unwrap();
    let (listening, server) = Server::standalone(|tokio| {
        let values = Rc::new(RefCell::new((HashMap::new())));
        Server::http(&addr, tokio)?.handle(move || Ok(AlphaBravo::new(&path)), tokio)
    }).unwrap();
    println!("Listening on http://{}", listening);
    server.run();
}
Или вот, еще:
            Put => {
                // If we didn't receive a key in the uri, we can do nothing.
                let values = splitter(&path);
                if values.len() != 1 {
                    Response::new().with_status(StatusCode::NoContent)
                } else if let Some(mut file) = self.rw.get_file(&values[0], true) {
                    match req.headers().get::<ContentLength>() {
                        Some(&ContentLength(len)) => {
                            // If there is no content, there is nothing to do.
                            if len < 1 {
                                Response::new().with_status(StatusCode::NotModified)
                            } else {
                                // The interesting part is here: for each chunk, we write it into
                                // the file.
                                return Box::new(req.body()
                                                   .for_each(move |chunk| {
                                    if file.write(&*chunk).is_ok() {
                                        Ok(())
                                    } else {
                                        Err(hyper::Error::Status)
                                    }
                                }).then(|r| match r {
                                      Ok(_) => Ok(Response::new().with_status(StatusCode::Ok)),
                                      Err(_) => Ok(Response::new().with_status(
                                                    StatusCode::InsufficientStorage)),
                                  }));
                            }
                        }
                        None => Response::new().with_status(StatusCode::NotModified),
                    }
                } else {
                    Response::new().with_status(StatusCode::InternalServerError)
                }
Имхо, даже в современных плюсах количество синтаксического оверхеда (с) как-то поменьше будет :(((

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

Что-то поменялось?

Да практически ничего. :) Стали писать на нём игори, серверную сторону сайтов, начали в него компилировать ну и в микроконтроллеры.

Неплохо для говноязычка, предназначенного только для того, чтобы бегущую строку на сайтике делать.

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

Местных

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

/me потянулся за следующим ведром попкорна

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

Памяти жрет значительно больше, это да. Go - и тот ест меньше.

Почему язык с рантаймом и гц жрет памяти меньше чем Rust? память - тоже важный фактор в вопросах производительности

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

Имхо, даже в современных плюсах количество синтаксического оверхеда (с) как-то поменьше будет :(((

Ну, ну

#include <iostream>
#include <string>
#include <boost/foreach.hpp>

int main(int argc, char* argv[]) {
  std::string str = "Boost FOREACH!";
  char ch;
  if (boost::foreach_detail_::auto_any_t _foreach_col11 = boost::foreach_detail_::contain( ( str ) , (true ? 0 : boost::foreach_detail_::or_( boost::foreach_detail_::and_( boost::foreach_detail_::not_(boost::foreach_detail_::is_array_( str )) , (true ? 0 : boost::foreach_detail_::is_rvalue_( (true ? boost::foreach_detail_::make_probe( str ) : ( str )), 0))) , boost::foreach_detail_::and_( boost::foreach_detail_::not_( boost_foreach_is_noncopyable( boost::foreach_detail_::to_ptr( str ) , boost_foreach_argument_dependent_lookup_hack_value) ) , boost_foreach_is_lightweight_proxy( boost::foreach_detail_::to_ptr( str ) , boost_foreach_argument_dependent_lookup_hack_value)) )) ) ) {} else if (boost::foreach_detail_::auto_any_t _foreach_cur11 = boost::foreach_detail_::begin( _foreach_col11 , (true ? 0 : boost::foreach_detail_::encode_type( str , boost::foreach_detail_::is_const_( str ))) , (true ? 0 : boost::foreach_detail_::or_( boost::foreach_detail_::and_( boost::foreach_detail_::not_(boost::foreach_detail_::is_array_( str )) , (true ? 0 : boost::foreach_detail_::is_rvalue_( (true ? boost::foreach_detail_::make_probe( str ) : ( str )), 0))) , boost::foreach_detail_::and_( boost::foreach_detail_::not_( boost_foreach_is_noncopyable( boost::foreach_detail_::to_ptr( str ) , boost_foreach_argument_dependent_lookup_hack_value) ) , boost_foreach_is_lightweight_proxy( boost::foreach_detail_::to_ptr( str ) , boost_foreach_argument_dependent_lookup_hack_value)) )) ) ) {} else if (boost::foreach_detail_::auto_any_t _foreach_end11 = boost::foreach_detail_::end( _foreach_col11 , (true ? 0 : boost::foreach_detail_::encode_type( str , boost::foreach_detail_::is_const_( str ))) , (true ? 0 : boost::foreach_detail_::or_( boost::foreach_detail_::and_( boost::foreach_detail_::not_(boost::foreach_detail_::is_array_( str )) , (true ? 0 : boost::foreach_detail_::is_rvalue_( (true ? boost::foreach_detail_::make_probe( str ) : ( str )), 0))) , boost::foreach_detail_::and_( boost::foreach_detail_::not_( boost_foreach_is_noncopyable( boost::foreach_detail_::to_ptr( str ) , boost_foreach_argument_dependent_lookup_hack_value) ) , boost_foreach_is_lightweight_proxy( boost::foreach_detail_::to_ptr( str ) , boost_foreach_argument_dependent_lookup_hack_value)) )) ) ) {} else for (bool _foreach_continue11 = true; _foreach_continue11 && ! boost::foreach_detail_::done( _foreach_cur11 , _foreach_end11 , (true ? 0 : boost::foreach_detail_::encode_type( str , boost::foreach_detail_::is_const_( str ))) ) ; _foreach_continue11 ? boost::foreach_detail_::next( _foreach_cur11 , (true ? 0 : boost::foreach_detail_::encode_type( str , boost::foreach_detail_::is_const_( str ))) ) : (void)0) if (boost::foreach_detail_::set_false( _foreach_continue11 )) {} else for ( ch = boost::foreach_detail_::deref( _foreach_cur11 , (true ? 0 : boost::foreach_detail_::encode_type( str , boost::foreach_detail_::is_const_( str ))) ) ; ! _foreach_continue11 ; _foreach_continue11 = true) {
    std::cout << ch;
  }
  std::cout << std::endl;
  return 0;
}

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

Потому что чукча — пейсатель:

это называется jemalloc

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

Имхо, даже в современных плюсах количество синтаксического оверхеда (с) как-то поменьше будет :(((

Исходники какого нибудь asio откройте, уважаемый.

Без комментариев

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

Смысл сравнения в том, что хотя в ржавчине (ещё) нет поддержки возможностей, которые есть в С++, его синтаксис уже выглядит не лучше.

Дык, это в обе стороны работает. В С++ нет лайфтаймов, а в расте они в заметной степени «портят синтаксис».

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

Не аналог, но пример.

#include <iostream>

#include <restinio/all.hpp>

auto request_handler()
{
	return []( auto req ) {
			if( restinio::http_method_get() == req->header().method() &&
				req->header().request_target() == "/" )
			{
				req->create_response()
					.append_header( "Server", "RESTinio hello world server" )
					.append_header_date_field()
					.append_header( "Content-Type", "text/plain; charset=utf-8" )
					.set_body( "Hello world!")
					.done();

				return restinio::request_accepted();
			}

			return restinio::request_rejected();
		};
}

int main()
{
	restinio::http_server_t<> http_server{
		restinio::create_child_io_service( 1 ),
		[]( auto & settings ){
			settings
				.port( 8080 )
				.address( "localhost" )
				.request_handler( request_handler() );
		}
	};

	http_server.open();

	std::cout << "http server on localhost:8080" << std::endl;
	int dummy;
	std::cin >> dummy;

	http_server.close();

	return 0;
}

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

fn start_server<P: AsRef<Path>>(addr: &str, path: P) {
    let addr = addr.parse().unwrap();
    let (listening, server) = Server::standalone(|tokio| {
        let values = Rc(RefCell((HashMap())));
        Server::http(&addr, tokio)?.handle(move || Ok(AlphaBravo(&path)), tokio)
    }).unwrap();
    println!("Listening on http://{}", listening);
    server.run();
}
Еще бы, конечно, неплохо было бы от unwrap-ов и ? избавиться. Но тут уж совсем глубокая родовая травма. Надо просто с этим жить.

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

А в Си++ как с корутинами дело обстоит? в какой реализации statefull? в бустовской?

Стандартных нет от слова совсем. Никаких. Пока. Нестандартные есть в Boost-е, ну и каждый для себя сам ваяет (вроде оных в synca).

Только речь шла про сравнение Rust-а и Go. Добавление в Rust stateless короутин не приблизит его к Go, имхо.

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

Я просил аналог.

Это и есть аналог, просто пример из ASIO умеет гораздо больше и у него, разумеется, другая архитектура.

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

Вы еще скажите, что вы этот код руками написали.

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

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

Хорошим тоном является вместо unwrap использовать try! и ?

Имхо, хорошим тоном было бы использовать общий подход к диагностике ошибок. А то вот здесь у нас паника:

let addr = addr.parse().unwrap();
а вот здесь уже возврат Result-а:
Server::http(&addr, tokio)?
а снаружи опять паника:
let (listening, server) = Server::standalone(...).unwrap();
Если в Rust-е настолько толерантно относятся к паникам, зачем заставлять людей постоянно вызывать unwrap явным образом. Почему бы не разделить addr.parse() и addr.parse_safe().unwrap()?

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

Да практически ничего. :)

Я про отношение.

начали в него компилировать

Это не от хорошей жизни делается.

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

Нет конечно, просто в сети нашёл, без шуток.

«Проблема с цитатами в Интернете в том, что им доверяют» В.И.Ленин (c)

Это какой-то шутник пропустил простой пример с использованием BOOST_FOREACH через препроцессор и запостил. К реальному плюсовому коду, тем более современному, это не имеет отношения.

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

/me потянулся за следующим ведром попкорна

Зато топики про Rust просто ракетой взлетают! :)))

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

Если в Rust-е настолько толерантно относятся к паникам, зачем заставлять людей постоянно вызывать unwrap явным образом.

unwrap - вообще, ИМХО для debug сделан и кстати при панике печатает call stack. try! при ошибке пробросить её выше и остановит дальнейшее выполнение этой функции. Если нужно прям в этой функции обработать ошибку, то используется match - ни кто не заставляет пихать unwrap во все щели. Я бы сделал, примерно так:

let addr = try!(parse().unwrap());
AntonyRF ★★★★
()
Ответ на: комментарий от anonymous

А производительность от этого не пострадает? Тогда зачем он нужен?

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

Это какой-то шутник пропустил простой пример с использованием BOOST_FOREACH через препроцессор и запостил. К реальному плюсовому коду, тем более современному, это не имеет отношения.

Да кто же спорит =) Просто как видно из примера (пусть и синтетического), оверхеда может быть дохерища.

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

Не совсем понял к чему этот пример (молчу про использование собственной либы, пример на бусте был бы веселее).

Ну и не вижу обработку ошибок. Их в этом коде быть не может?

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

зачем заставлять людей постоянно вызывать unwrap явным образом.

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

Местами, кстати, разделение на две функции имеется.

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

вызывал бы у меня гораздо меньше неприятия

А что изменилось?

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

Мне кажется, у вас пробелы в знании Rust-а сравнимы с моими. AFAIK, try!(func) это макрос, который делает что-то вида:

match func() {
  Ok(v) => v;
  Err(e) => return Err(e);
}
А вот unwrap возвращает либо нормальное значение, либо бросает панику. Поэтому обрамлять unwrap() макросом try! нет смысла.

Надеюсь, опытные Rust-оманы меня поправят.

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

Быстро вы слились.

Ты идиот что ли? Не можешь понять, что https://github.com/tokio-rs/tokio-minihttp/tree/master/src умеет меньше, чем http://www.boost.org/doc/libs/1_64_0/doc/html/boost_asio/example/cpp11/http/s... и имеет другую архитектуру (а на ошибки просто забивает)?

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

Вы не в состоянии понять какие вопросы вам задают?

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

Не совсем понял к чему этот пример

Может потому, что вы тупой?

Ну и не вижу обработку ошибок.

Это потому, что вы не только тупой, но еще и малограмотный. Исключения в C++ном коде приводят к такому же эффекту, как и unwrap-ы в Rust-е. Но, т.к. со знаниями C++ у вас проблемы, то вы и не видите.

А что изменилось?

Нет нахер никому не нужных ::new().

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