Команда Rust рада представить выпуск Rust 1.21.0. Rust — это системный язык программирования, нацеленный на скорость, безопасность и параллельное выполнение кода.
Если у вас установлена предыдущая версия Rust, для обновления достаточно выполнить:
$ rustup update stable
Если же у вас еще не установлен rustup
, вы можете установить его с соответствующей страницы нашего веб-сайта. С подробными примечаниями к выпуску Rust 1.21.0 можно ознакомиться на GitHub.
Что вошло в стабильную версию 1.21.0
Этот выпуск содержит несколько небольших, но полезных изменений языка и новую документацию.
Первое изменение касается литералов. Рассмотрим код:
let x = &5;
В Rust он аналогичен следующему:
let _x = 5;
let x = &_x;
То есть 5
будет положено в стек или возможно в регистры, а x
будет ссылкой на него.
Однако, учитывая, что речь идет о целочисленном литерале, нет причин делать значение таким локальным. Представьте, что у нас есть функция, принимающая 'static
аргумент вроде std::thread::spawn
. Тогда вы бы могли использовать x
так:
use std::thread;
fn main() {
let x = &5;
thread::spawn(move || {
println!("{}", x);
});
}
Этот код не соберется в прошлых версиях Rust'а:
error[E0597]: borrowed value does not live long enough
--> src/main.rs:4:14
|
4 | let x = &5;
| ^ does not live long enough
...
10 | }
| - temporary value only lives until here
|
= note: borrowed value must be valid for the static lifetime...
Из-за локальности 5
, ссылка на него тоже живет слишком мало, чтобы удовлетворить требованиям spawn
.
Но если вы соберете это с Rust 1.21, оно заработает. Почему? Если что-то, на что создана ссылка, можно положить в static
, мы могли бы "обессахарить" let x = &5;
в нечто вроде:
static FIVE: i32 = 5;
let x = &FIVE;
И раз FIVE
является static
, то x
является &'static i32
. Так Rust теперь и будет работать в подобных случаях. Подробности смотрите в RFC 1414, который был принят в январе 2017, но начинался еще в декабре 2015!
Теперь мы запускаем LLVM параллельно с кодогенерацией, что должно снизить пиковое потребление памяти.
RLS теперь может быть установлен через rustup вызовом rustup component add rls-preview
. Много полезных инструментов, таких как RLS, Clippy и rustfmt
, все еще требуют ночной Rust, но это первый шаг к их работе на стабильном канале. Ожидайте дальнейших улучшений в будущем, а пока взгляните на предварительную версию
Теперь об улучшениях документации. Первое: если вы посмотрите на документацию модуля std::os
, содержащего функционал работы с операционными системами, вы увидите не только Linux
— платформу, на которой документация была собрана. Нас долго расстраивало, что официальная документация была только для Linux. Это первый шаг к исправлению ситуации,
хотя пока что это доступно только для стандартной библиотеки, а не для любого пакета (crate). Мы надеемся исправить это в будущем.
Далее, документация Cargo переезжает! Исторически документация Cargo была размещена на doc.crates.io, что не следовало модели выпусков (release train model), хотя сам Cargo следовал. Это приводило к ситуациям, когда какой-то функционал скоро должен был "влиться" в ночной Cargo, документация обновлялась, и в течение следующих 12 недель пользователи думали, что все работает, хотя это еще не было правдой. https://doc.rust-lang.org/cargo будет новым домом для документации Cargo, хотя сейчас этот адрес просто перенаправляет на doc.crates.io. Будущие выпуски переместят настоящую документацию Cargo, и тогда уже doc.crates.io будет перенаправлять на doc.rust-lang.org/cargo. Документация Cargo уже давно нуждается в обновлении, так что ожидайте еще новостей о ней в скором будущем!
Наконец, до этого выпуска у rustdoc
не было документации. Теперь это исправлено: добавлена новая книга "rustdoc
Book", доступная по адресу https://doc.rust-lang.org/rustdoc. Сейчас эта документация очень примитивна, но со временем она улучшится.
Подробности смотрите в примечаниях к выпуску.
Стабилизации в стандартной библиотеке
В этом выпуске не так много стабилизаций, но есть кое-что, очень упрощающее жизнь: из-за отсутствия обобщения относительно целых чисел (type-level integers), массивы поддерживали типажи только до размера 32. Теперь это исправлено для типажа Clone
. Кстати, это же вызывало много ICE (внутренних ошибок компилятора), когда тип реализовывал только Copy
, но не Clone
.
Для других типажей недавно был принят RFC об обобщении относительно целых чисел, который должен исправить ситуацию. Это изменение еще не реализовано, но подготовительные работы уже ведутся.
Затем был стабилизирован Iterator::for_each
, дающий возможность поглощать итератор ради побочных эффектов без for
цикла:
// старый способ
for i in 0..10 {
println!("{}", i);
}
// новый способ
(0..10).for_each(|i| println!("{}", i));
Какой из способов лучше зависит от ситуации. В коде выше for
цикл прост, но, если вы выстраиваете цепочку итераторов, версия с for_each
может быть понятнее:
// старый способ
for i in (0..100).map(|x| x + 1).filter(|x| x % 2 == 0) {
println!("{}", i);
}
// новый способ
(0..100)
.map(|x| x + 1)
.filter(|x| x % 2 == 0)
.for_each(|i| println!("{}", i));
Стабилизированы функции max
и min
типажа Ord
.
Стабилизирована встроенная функция (intrinsic) needs_drop
.
Наконец, был стабилизирован std::mem::discriminant
, позволяющий вам узнать активный вариант перечисления без использования match
оператора.
Подробности смотрите в примечаниях к выпуску.
Функционал Cargo
Помимо вышеупомянутых изменений документации Cargo в этом выпуске получает большое обновление: [patch]
. Разработанная в RFC 1969, секция [patch]
вашего Cargo.toml
может быть использована, когда вы хотите заменить части вашего графа зависимостей. Это можно было сделать и раньше, посредством [relace]
. Если коротко, то [patch]
это новый и более удобный [replace]
. И хотя у нас нет планов убирать или объявлять устаревшим [replace]
, вам скорее всего стоит использовать именно [patch]
.
Как же работает [patch]
? Допустим, у нас есть такой Cargo.toml
:
[dependencies]
foo = "1.2.3"
Так же наш пакет (crate) foo
зависит от пакета bar
, и мы нашли ошибку в bar
. Чтобы проверить это, мы скачиваем исходный код bar
и обновляем наш Cargo.toml
:
[dependencies]
foo = "1.2.3"
[patch.crates-io]
bar = { path = '/path/to/bar' }
Теперь при выполнении cargo build
будет использована наша локальная версия bar
, а не версия из crates.io
, от которой на самом деле зависит foo
.
Подробности смотрите в документации.
Также:
Теперь вы можете использовать cargo install
для одновременной установки сразу нескольких пакетов.Аргумент --all
автоматически добавляется к команде, если вы в виртуальном рабочем пространстве (virtual workspace).Поля include
иexclude
в вашемCargo.toml
теперь принимают шаблоны аналогичные.gitignore
.
Подробности смотрите в примечаниях к выпуску.
Разработчики 1.21.0
Множество людей участвовало в разработке Rust 1.21. Мы не смогли бы этого добиться без участия каждого из вас. Спасибо!
От переводчика: Благодарю sasha_gav и vitvakatu за помощь в переводе
Комментарии (26)
helgihabr
17.10.2017 10:12>> То есть 5 будет положено в стек или возможно в регистры, а x будет ссылкой на него
Можно ли точно знать куда будет положено значение (в стек или же в регистр) или здесь нет четкого правила?mayorovp
17.10.2017 10:21+2Зависит от того, сможет ли оптимизатор кода заинлайнить его до такой степени что ссылка станет не нужна. Если сможет — то значение будет кандидатом на передачу через регистр, если вообще останется после всех оптимизаций. Если не сможет — то конечно же оно попадет в стек, потому что на регистр ссылку передать нельзя.
ozkriff Автор
17.10.2017 13:18+1На уровне языка, насколько я знаю — нельзя.
Но если очень надо, можно, как и в других системных языках, для конкретного приложения на конкретной архитектуре дизасм посмотреть и попытаться подогнать код так, что бы оптимизатор затолкал что нам там надо в регистр. Только с этим, конечно, надо быть осторожным и много раз перепроверить что в итоге вышла реально оптимизация — а то LLVM умный, его не так-то легко перехитрить руками обычно.
beduin01
Rust с каждой версией все более и более уродским становится.
Вот хороший пример читаемости синтаксисов:
Go:
C++
Rust:
D:
ozkriff Автор
А в чем отличие читаемости rust 1.21 от 1.20?
О.о Теплое с мягким.
beduin01
>А в чем отличие читаемости rust 1.21 от 1.20?
Да ничем. Она отсутствует.
mayorovp
Вообще-то, в Rust приведенная вами строчка при желании сохранить читаемость пишется как
let x = &0;
. Но даже в таком виде она на самом деле является бессмысленной, так никто не пишет; в реальности эта фича нужна при применении каких-нибудь обобщенных алгоритмов к примитивам.pftbest
Вам на ЛОРе уже писали что сравнение неверное, эквивалентный код будет
Но даже если действительно нужно воспользоваться лайфтаймами, то все равно код будет короче, потому что треугольные скобки указывать в данном случае не обязательно:
phponelove
Не верно. На лоре в первых же сообщениях пример был изменён.
Манипуляция. А так будет ещё короче:
И?
То, что мы нормальные имена заменили на a и назвали это «нормальным» — это ничего не меняет. А если туда добавить generics? А если добавить ещё один лайфтайм? Во что превратиться это?
ozkriff Автор
А можно тогда привести пример этого исправленного адекватного сравнения?
А синтаксис ржавчины тут причем? В любом языке при добавлении качественно отличающейся информации размер сигнатуры функции (да и любой другой записи) будет увеличиваться.
Я так понимаю что придирка сводится к тому что ржавчина умеет явно выражать ВЖ, а большинство других языков — нет? Тогда, надо выбрать победителем питон и успокоиться.
phponelove
Зачем вы пытаетесь создавать себе «пути к отступлению» вводя какие-то непонятные критерии, которых ранее не было?
Подтверждение моим словам есть — www.linux.org.ru/forum/development/13699421#comment-13699478
Притом, что в других языках никаких лайвтаймов нет и если в крестах есть 2 вида аргументов, то тут есть уже три.
Не верно. В расте это дополнительный ахтунг, которого в другом языке(без лайвтаймов) не будет.
Это не придирка. Раст ввёл в язык некую новую сущность, которая не бесплатна с т.з. читаемости и ой как не бесплатна. Да и синтаксис очень спорен — это как минимум.
А люди почему-то выдают её за бесплатную. В этом и есть проблема.
Это глупость. И почему — ниже так же объяснили на том же лоре.
И ваш пример крайне показателен. Вы не спорите с тем, что явная типизация не бесплатна, а в ситуации с растом это не так. Тип — это свойство переменной, который не указывается явно в питоне. Точно так же лайвтайм — это такое же дополнительное свойство. Т.е. ситуация аналогичная, но в случае с растом у нас «всё так же»( что явно неправда), а в случае с питоном всё не так же.
Таким образом питон < С++ < раст. Критерий ясен и понятен. С питоне только имя, в С++ имя и тип, в расте имя + тип + лайвтайм.
Это явная манипуляция о которой я и говорил.
ozkriff Автор
Потому что сравнение в первом комментарии этой темы очевидно бессмысленно — третьесортный толстый троллинг с лора — и тратить время на анализ подобного не хочется.
Я не вижу что бы кто-то в этой теме заявлял о полной бесплатности ВЖ для читателя кода. Явные ВЖ усложняют типы, только я считаю что это окупается более выразительной формальной семантикой — так и сам код понимать проще, и оптимизации дополнительные можно провести, и от кучи ошибок с памятью автоматически защищает.
Первый комментарий был про уродство ржавого синтаксиса — вот с этим я не согласен, я уверен что это просто вопрос привычки.
Обсуждений было много, лучше так никто и не предложил.
… И там нет возможности выразить эту полезную в некоторых ситуациях информацию, разве нет?
phponelove
Для вас всё неудобное троллинг. Очень удобно, не спорю.
Выше, пожалуйста. Никто из ваших оппонентов не обязан что-то знать про ваш «язык». Приводят примеры и если что-то в них немного неверно написано, то их этого ничего не следует. Это ваша обязанность адаптировать примеры.
А то получается как «будет то же самое» и всё очень удобное — можно посмеяться над автором, но суть примера не в этом. Об этом в том треде сообщили и я вам тут сообщил, что вы, благополучно, проигнорировали. Очень удобно, повторю.
>> Явные ВЖ усложняют типы, только я считаю что это окупается более выразительной формальной семантикой — так и сам код понимать проще, и оптимизации дополнительные можно провести, и от кучи ошибок с памятью автоматически защищает.
Вся эта «более выразительная» — ничего общего с объективностью не имеет. Это итак ясно из контекста.
Наличие подобно в расте лишь следствие слабости компилятора и не более того. А все эти рассуждения об каких-то мистических оптимизациях ничего так же не стоят без примеров. Я их не видел, вы их не показали. Нет никаких оснований вам верить. Всё просто.
Этим можно оправдать всё что угодно. Следует ли из этого что-то? Нет. Ваши слова значат не более чем его, либо чем мои. Никаких объективных свидетельств нету.
Из этого так же ничего не следует. Отсутствие предложения в какой-то определённой тусе мало что значит. Да и какое там могло быть обсуждение, если это просто рандомный символ на подобии *&. Что там остаётся? #@'? По мне так ' самое глупое из них. По причине 'a' ~= 'a.
Да и странно как-то обсуждалось всё это. Был ГЦ, был другой синтаксис. Потом вдруг бам. ГЦ не стало, ГЦ стал не моден. Идеальный синтаксис выпилили и взяли крестовый.
>>… И там нет возможности выразить эту полезную в некоторых ситуациях информацию, разве нет?
Ещё раз. Она там только по причине слабости компилятора. Никакой новой информации она не даёт — она итак выводится из контекста.
То же самое с автоматическим выводом типов. Оно не теряет типы. И нужно не для какой-то там неведомой «полезной информации», а чисто для ограничений, либо кастов всяких литералов в нужный тип.
Если у меня есть функция (a) -> b {return c{a};}, где с хранит ссылку a, но при этом это компилятор сам вывести не может — это никакой новой информации мне не даёт, да и вообще не даёт.
0xd34df00d
По значению, по указателю, по lvalue-ссылке, по rvalue-ссылке. Какие два вида из этих вы имели в виду?
И это я ещё про шаблонную наркоманию не говорю.
А в языке без статических типов вообще типов не будет. Щастье-то какое!
Тип в питоне — это свойство объекта в рантайме, который скрывается за переменной. Статических типов там нет. К сожалению.
Во-первых, такой код сложнее поддерживать (где нет статических типов).
Во-вторых, куда вы там впихнёте какой-нибудь хаскель, например?
red75prim
'a
в данном случае никакого собственного смысла не несёт, единственная его функция — указать одинаковость времён жизниarch
и возвращаемого значения. Поэтому нет никакого смысла придумывать для него длинное имя.Для сравнения, в Haskell аннотации типов обычно тоже однобуквенные:
(a -> b) -> a -> b
. Потому что нет никакого смысла писать(любойтип1 -> любойтип2) -> любойтип1 -> любойтип2
. Так и здесь нет никакого смысла писать'любое_подходящее_время_жизни_1
phponelove
Никакого собственного смысла имя типа в шаблонах не несёт, единственная его функция — указать одинаковость типов…
Никакого собственного смысла переменная не несёт, единственная её функция — указать одинаковость значений…
Ну и ключевое слово «в данном случае». А не в данном?
>>Для сравнения, в Haskell аннотации типов обычно тоже однобуквенные
И? Что же из этого следует? Т.е. вы мне предлагаете писать так: а<A, 'a, B, C>(arg0: &'a A, arg1: &'a B) -> &'a B? Либо как там у вас будет? Нет, спасибо.
Т.е. вместо (Container, Value) -> Iterator мне надо писать (a, b) -> c? Нет, спасибо. Проблема в том, что там не любой тип — любой тип никому не нужен.
red75prim
Из этого следует, что не стоит напрягаться и придумывать описание для вещей, которые в описании не нуждаются. Переменные обычно связаны с семантикой выполняемых функцией действий, поэтому имеет смысл называть их так, чтобы семантика функции становилась яснее. Функции аннотаций времен жизни — связывание времен жизни различных переменных отношением равенства и задание отношения "живёт-дольше-чем" между двумя временами жизни. Как ни назови
'a
, его значение будет "ссылка X живет столько же сколько ссылка Y", а это и так видно из кода.Требовать назвать время жизни осмысленней, примерно то же самое, что отказываться решать уравнение 2*x+4=0, пока вместо x не поставят "неизвестное_количество_яблок" или что-то в этом роде.
Это совершенно разные сигнатуры функций. В Haskell идентификаторы используемые в сигнатуре функции и начинающиеся со строчной буквы обозначают именно любой тип.
В C++ это будет
template<a,b,c> c func(std::pair<a,b> val){...}
иIterator func(std::pair<Container, Value> val)
— разные вещи, в общем.Кстати, функция с сигнатурой
(a,b) -> c
довольно бессмысленна, так как может возвращать только bottom. Но это уже к обсуждению не относится.phponelove
Явное противоречие. Вы уж определитесь с тем — нужно ли вводить ассоциацию между значением и тем, что оно выражает, либо нет. А то в одном случае смысла нет, а в другом есть.
Я уже давал вам вариации на тему, которые вы, благополучно, проигнорировали. Как не назови A, его значение будет «тип ссылки X является тем же, что у типа ссылки Y».
Я могу попытаться найти какое-то рациональное зерно в ваших рассуждения и сформулировать его. Переменные — это отображение неких сущностей из реальной мира, либо каких-то внешних моделей. Есть модель тех же уравнений — там у x есть чёткое определение, только оно определённо где-то в рамках этой модели. И записывая 2x+4=0 вы просто используете x как экспорт из этой модели. И уже каждый знает что такое x.
А вот если вы там напишите 2*g+4=0, то вы получите фейл. Т.к. вы привели банальный подлог.
Поэтому, если мы считаем яблоки, то у нас и название переменных будут яблоки, а не x — x это некая обобщённая форма неизвестной. Как то же value. И использовать везде abc вместо яблок — это моветон.
Тут имеет смысл только ссылка на то, что лайвтаймы — это про отношения ссылок/сущностей внутри самого языка. Мы не экспортируем что-то явно, либо по умолчанию( как в примере с x), а именно определяем их в определённом контексте.
Но опять же — это тупиковый путь. open_file(string a); — почему подобное является моветоном? Ведь контекст явно определён. file, да и тип string из которого мы сразу узнаём искомое свойство файла, но. Это некрасиво, это глупо.
Поэтому да, явно видно к чему привязан лайвтайм, но. Явно видно и то, что a = x * y. Но что дальше? По мне так лучше это назвать xy, пусть мы итак видим, что это x * y. Явно видно, что string a = file.name; Но что из этого следует? Да ничего, кроме того, что это явная глупость.
То же самое с лайвтаймами. Смотря на 'a — я не понимаю что это и откуда взялось. Мне надо смотреть на полный контекст, который может быть не таким примитивным. Зачем? А незачем. И причину я уже описал выше. А далее я делаю вывод, что вы используете это потому, что лайвтаймы — это введённые для компилятора кастыли. Вы итак видите продление времени жизни объекта — это вам не нужно. Именно поэтому вы и пытаетесь их писать write-only и после — игнорировать. Иначе подобное поведение не описать.
Таким образом это что-то типа. let a; a = b; c = a; — т.е. у меня есть a', которое никак и ни к чему не привязано. Оно болтается в воздухе. Далее, оно неизвестно где и неизвестно как привязывается ко времени жизни одного и второго(и далее) объекта как-бы уравнивая время их жизни. И ты никак из этого 'a не узнаешь что это, зачем это и почему.
Какой смысл в этом по сравнению с (a, b) -> c {return c{move(a)};} — я не вижу.
Я уже заранее ответил на это, но вы почему-то это проигнорировали. Никакие «любые типы» НИКОМУ и НИКОГДА не нужны. Это очень и очень редкий кейс.
Это вещи идентичные. Либо вы не понимаете, что это шаблонные типы?
template<Container, Value, Iterator> Iterator func(std::pair<Container, Value> val); А не то, что вы написали.
Здесь у нас есть некий обобщённый(упрощённый) тип контейнера, который подразумевает контейнер, а не хрен пойми что. Некий обобщённый тип значения и некий обобщённый тип итератора. Т.е. имя наделяет переменную дополнительными свойствами, которых не на уровне языка. Но не только.
Существует нотации для определения свойств уровня языка в переменных. Кто-то их считается осмысленными и нужными. Да и много есть кейсов, где имя удобнее.
Т.е. даже если у меня есть механизмы внутри самого языка ограничить множество типов в a только контейнерами, то мне проще использовать Container, а не смотреть что там и как определенно. Особенно если это тонна лапши. Это лишь дополнительная строгость уровня языка…
Что? В этом и проблема, что вы уже запутались. А если бы вы посмотрели на имена — вы бы сразу там увидели банальную сигнатуру find(a, b) -> c.
mayorovp
Нетривиальной функции
template<Container, Value, Iterator> Iterator func(std::pair<Container, Value> val);
которая бы делала что-то осмысленное в языке С++ существовать не может. Дальше думайте сами.phponelove
Слив засчитан.
Что это за бредни? Во-первых откуда там взялся pair? Во-вторых — я уже назвал пример.
В любом случае — исход был заранее определён. Вы очень часто путаете ваши мечты и ваши возможности. Ваши возможности ограничены минусом «в крысу». В следующий раз не надо это путать. Остальные вон не перепутали.
mayorovp
Вообще-то это была цитата из вашего же комментария. Слив засчитан :-)
0xd34df00d
Разница между хаскелем и плюсами в том, что плюсы тайпчекаются после мономорфизации, поэтому технически может.
Вы можете написать тело этой функции так (с точностью до неопределимости компилятором Iterator, но это к делу не очень относится, вероятно), будто вам передадут пару только из, скажем, вектора строк и строки. Если эту функцию действительно вызовут в таком контексте, то всё будет хорошо.
Более того, вы можете считать, что вам передадут, скажем, вектор объектов с определённым оператором сравнения и объект того же типа (и тогда её даже имеет смысл делать темплейтом), и пока функцию будут вызывать в этом контексте, тоже всё будет хорошо.
Иными словами, совсем коротко:
mayorovp
Я не случайно написал что функция должна делать что-то осмысленное.
0xd34df00d
В вашем случае она вполне может искать второй элемент пары в первом, например.
mayorovp
Независимо от того что она делает на самом деле, Iterator следует убрать из параметров шаблона и заменить на
Container::iterator
ozkriff Автор
кстати, по этой логике победителем должен быть питон, почему его (или еще какой скриптоты) даже в списке нет :(?
def typeList(arch, offset, parseDecl):
(это я тонко намекаю на потерю информации)