LINUX.ORG.RU

Rust 1.16

 ,


2

7

Представлен релиз Rust 1.16 — системного языка программирования, нацеленного на безопасную работу с ресурсами, скорость и параллельное выполнение кода. В этот релиз вошли 1364 патча от 137 разработчиков.

Если у вас уже установлена предыдущая версия Rust, то обновиться до Rust 1.16 очень легко:

$ rustup update stable

Самое крупное нововведение в Rust 1.16 — новая субкоманда cargo check направленная на ускорение процесса разработки.

Что она делает? Давайте рассмотрим как rustc компилирует код. Компиляция происходит в несколько проходов, шагов от исходного кода до исполняемого файла. Все эти шаги (и затраченные на них ресурсы) можно увидеть, передав компилятору ночной ветки -Z time-passes

rustc .\hello.rs -Z time-passes
time: 0.003; rss: 16MB  parsing
time: 0.000; rss: 16MB  recursion limit
time: 0.000; rss: 16MB  crate injection
time: 0.000; rss: 16MB  plugin loading
time: 0.000; rss: 16MB  plugin registration
time: 0.049; rss: 34MB  expansion
Мысленно эти шаги можно разбить на два больших: сначала rustc совершает все проверки безопасности, убеждается в корректности синтаксиса, и всё такое, а после он выполняет оптимизации и генерирует бинарный код.

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

  • Пишем немного кода.
  • Запускаем cargo build, чтобы проверить, что код компилируется.
  • Повторяем предыдущие шаги N раз.
  • Запускаем cargo test, чтобы убедиться, что все тесты проходят успешно.
  • Возвращаемся в начало.

Во втором шаге код никогда не запускается. Там нужен результат анализа кода от компилятора, а не запускаемый бинарник. cargo check создана именно для этого случая: команда запускает все проверки компилятора, не тратя время на генерацию бинарного кода.

Другие улучшения

Для работы cargo check компилятор научился генерировать новый формат файлов: .rmeta. Такие файлы содержат метаданные о каком-либо одном крейте. cargo check использует эти данные для анализа зависимостей проекта и позволяет компилятору сделать проверки типов из сторонних библиотек.

Удалена диагностика consider using an explicit lifetime parameter, появляющаяся если в коде были использованы неправильные аннотации времени жизни. Рассмотрим пример:

use std::str::FromStr;

pub struct Name<'a> {
    name: &'a str,
}

impl<'a> FromStr for Name<'a> {
    type Err = ();

    fn from_str(s: &str) -> Result<Name, ()> {
        Ok(Name { name: s })
    }
}
Здесь компилятор не уверен, что делать с временем жизни: код не гарантирует что s проживет столько же, сколько Name, как того требуется. Компилятор Rust 1.15 выведет:
> rustc +1.15.1 foo.rs --crate-type=lib
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter in generic type due to conflicting requirements
  --> .\foo.rs:10:5
   |
10 |       fn from_str(s: &str) -> Result<Name, ()> {
   |  _____^ starting here...
11 | |         Ok(Name { name: s })
12 | |     }
   | |_____^ ...ending here
   |
help: consider using an explicit lifetime parameter as shown: fn from_str(s: &'a str) -> Result<Name, ()>
  --> .\foo.rs:10:5
   |
10 |       fn from_str(s: &str) -> Result<Name, ()> {
   |  _____^ starting here...
11 | |         Ok(Name { name: s })
12 | |     }
   | |_____^ ...ending here
Компилятор описывает проблему и дает рекомендации по исправлению. Модифицируем код согласно им и пробуем снова:
> rustc +1.15.1 .\foo.rs --crate-type=lib
error[E0308]: method not compatible with trait
  --> .\foo.rs:10:5
   |
10 |       fn from_str(s: &'a str) -> Result<Name, ()> {
   |  _____^ starting here...
11 | |         Ok(Name { name: s })
12 | |     }
   | |_____^ ...ending here: lifetime mismatch
   |

help: consider using an explicit lifetime parameter as shown: fn from_str(s: &'a str) -> Result<Name<'a>, ()>
  --> .\foo.rs:10:5
   |
10 |       fn from_str(s: &'a str) -> Result<Name, ()> {
   |  _____^ starting here...
11 | |         Ok(Name { name: s })
12 | |     }
   | |_____^ ...ending here
Всё ещё не работает. Предлагается добавить еще одно время жизни, на этот раз для Name
> rustc +1.15.1 .\foo.rs --crate-type=lib

help: consider using an explicit lifetime parameter as shown: fn from_str(s: &'a str) -> Result<Name<'a>, ()>
  --> .\foo.rs:10:5
...и наконец код скомпилировался.

Мотивация для этой диагностики была правильной, но когда диагностика ошибалась, они ошибалась сильно. Иногда предлагался даже некорректный синтаксис. Более того, опытным пользователям Rust эта диагностика не нужна, а новичков она сбивала с толку, поэтому было принято решение ее убрать. Возможно, в будущем диагностика снова вернется, если получится уменьшить количество ложно-положительных срабатываний.

Есть и другие изменения, касающиеся диагностик. Предыдущая версия Rust уже научилась предлагать исправления опечаток:

let foo = 5;

println!("{}", ffo)
error[E0425]: cannot find value `ffo` in this scope
 --> foo.rs:4:20
  |
4 |     println!("{}", ffo);
  |                    ^^^ did you mean `foo`?
Но это происходило только для опечаток в локальных переменных и полей в структурах. Сейчас Rust предлагает исправления опечаток почти везде.

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

Также стабилизированы некоторые мелкие улучшения:

  • writeln! теперь можно вызывать без аргументов, как println!, для перевода строки.
  • Все структуры в стандартной библиотеке теперь реализуют Debug

Улучшен формат ошибок при получении среза &str. Например:

&"abcαβγ"[..4]
Этот код содержит ошибку:
thread 'str::test_slice_fail_boundary_1' panicked at 'byte index 4 is not
a char boundary; it is inside 'α' (bytes 3..5) of `abcαβγ`
Часть после ; — новая.

Возможности Cargo

Дополнительно к cargo check, Cargo и crates.io получили пару новых возможностей.

  • cargo build и cargo doc теперь принимают флаг --all для сборки и документации всех крейтов в рабочем пространстве одной командой.
  • Cargo теперь принимает флаги --version --verbose, по образу rustc.
  • Crates.io может отображать значки AppVeyor и TravicCI на странице крейта.
  • В Cargo и crates.io появились категории. В отличие от ключевых слов, категории модерируются. Ключевые слова используются для поиска, категории — нет. Другими словами, ключевые слова помогают искать крейты, а категории — просматривать.
  • На crates.io теперь можно просматривать крейты по категориям.

Больше информации можно узнать в развернутых release notes.

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

★★★★★

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

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

Хранить по 4 байта на символ не выгодно даже для русских и китайских букв, не только для ascii. А если хранить по 2 байта на символ (UTF-16), то интерфейс придется делать такой же как для UTF-8 (с вызовом функций) плюс оверхед по памяти для ascii, так что в двойне не выгодно.

Спасибо, я знаю что такое юникод

А в ассемблере все эти функции полностью инлайнятся, так что код получается очень эффективный.

Только вот набирают не в виде ассемблера

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

Мне - нет. Поэтому я думаю, что это нетипичная задача.

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

Ну а почему тогда не возвращать null(или как это в расте завётся)?

Потому что из оператора квадратные скобки можно возвращать только ссылки:

fn index(&self, index: Idx) -> &Self::Output;
Это было сделано для удобства работы с векторами и слайсами. Никого ведь не удивляет то что если взять элемент за пределами массива то произойдет ошибка. Просто у строк есть еще одно дополнительное ограничение.

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

Системным программистам не приходится работать с частями строк?

Системный С уже полвека живёт с нультерминированными строками, из которых безболезненно можно только подстроку в конце брать. В стандартной библиотеке C++ появился аналог строкового слайса только в C++17. В общем, похоже что так и есть, не особо-то удобные методы взятия подстроки кому-то нужны.

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

И что делать если функция None вернёт? По логике нужно либо упасть, либо вернуть ошибку, либо поправить индекс и попытаться снова. Первый вариант удобно решается текущим оператором взятия среза, для второго и третьего у str есть метод is_char_boundary.

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

С частями строк rust нормально работает. Просто срез для других задач. Кодировка консоли UTF-8.

#cat test.rs
fn main() {

    let s : &str = "абвгдежзиклмн";
    println!("{}", s);

    let ss = s.chars().nth(1);
    println!("{:?}", ss);

    let sss : String = s.chars().skip(2).take(4).collect();
    println!("{}", sss);

}

# ./test
абвгдежзиклмн
Some('б')
вгде

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

Ты не путай человека, get есть только для слайсов а не для строк.

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

Ну а почему тогда не возвращать null(или как это в расте завётся)?

Потому что нетипизированный null - абсолютное зло, и в Расте его нет и не будет (не путать с типизированными нулями вроде нулевого указателя).

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

Бгг.

Что такого забавного? nullptr и эквиваленты в Расте и прочих языках - это тип указателя, семантика такая же как и у других указалелей. Монады Some/None тоже вполне ясно воспроизводят свойства множества, их свойства и корректность использования явно предусмотрены компилятором.

А нетипизированный NULL хорошо воспроизводит только segmentation fault.

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

А нетипизированный NULL хорошо воспроизводит только segmentation fault.

Типизированный NULL воспроизводит его не хуже. И да, Хоар говорил именно о типизированном нуле.

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

Чем в данном примере «не хуже», если код, возвращающий Option, явно предусматривает вариант Some и None, и заставляет программиста написать обрабатывающий код для обоих вариантов, в противном случае он не компилируется? А не валиться с сегфолтом потому, что _внезапно_ вместо элемента массива оказался NULL.

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

Причем здесь Option? Это не тип указателя.

Браво! Вы угадали! Это *действительно* не тип указателя! Более того, это сможет угадать не только Вы, но и компилятор! :-)

В то время как, получив нетипизированный NULL, это будет угадывать рантайм.

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

Причем здесь Option? Это не тип указателя.

Браво! Вы угадали!

Нет.

В то время как, получив нетипизированный NULL, это будет угадывать рантайм.

У тебя какое-то свое, глубоко личное, понимание термина «нетипизированный NULL».

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

Человек просто криво описал ситуацию, когда информация, может ли указатель содержать нуль описывается в статическом типе. Ничего реализовывать не нужно, Option<&T> или Option<Box<T>>. На этапе компиляции всё это перейдёт в обычные указатели, с нулевым значением указателя для варианта None.

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