Hello, World!

Итак, когда Rust уже установлен можно приступать к написанию вашей первой программы. По традиции, (а точнее с 1978 года, когда вышло в свет первое издание книги о Си) напишем небольшую программу, которая напечатает "Привет, Мир!" в строке вывода.

Обратите внимание, что читатели должны быть знакомы с командной строкой. Язык Rust не требует каких-то специальных настроек редакторов исходного кода, настройкам инструментальных средств. IDE экономят время и если ещё дают возможность тонкой настройки параметров запуска утилит - это замечательно. Как бы там ни было, программист должен знать и уметь пользоваться командной строкой и мы этому научим.

Создание папки проекта

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

Т.к. команды в терминалах Linux и Mac идентичны, предлагаю, для краткости, писать Unix, в тех случаях, когда мы будем описывать работу в терминале Linux или Mac.

Итак, приступим. Создадим папку projects и далее будем создавать проекты Rust в этой директории. Для этого откроем программу-терминал и введём следующие команды:

Unix:

$ mkdir ~/projects
$ cd ~/projects
$ mkdir hello_world
$ cd hello_world

Windows CMD:

> mkdir %USERPROFILE%\projects
> cd %USERPROFILE%\projects
> mkdir hello_world
> cd hello_world

Windows PowerShell:

> mkdir $env:USERPROFILE\projects
> cd $env:USERPROFILE\projects
> mkdir hello_world
> cd hello_world

Написание и запуск первой программы

Далее, создадим текстовый файл и назовём его main.rs.

Все файлы исходного кода Rust имеют расширение .rs (ещё одна конвенция, упрощающая программирование).

Рекомендуем использовать символ подчёркивания для разделения слов в многословных названиях. Во-первых, такие названия легче читать, а во-вторых, это упрощает работу утилит. Пример: i_like_hello_world.rs.

Теперь откроем файл main.rs для редактирования и введём следующие строки кода:

Filename: main.rs

fn main() {
    println!("Hello, Rust world! Привет, Мир!");
}

Сохраним файл и вернёмся в окно терминала. Введём следующие (две) команды:

Unix:

$ rustc main.rs # нажмите клавишу Enter
$ ./main # нажмите клавишу Enter
Hello, Rust world! Привет, Мир!

Windows CMD:

> rustc main.rs # нажмите клавишу Enter
> main
Hello, Rust world! Привет, Мир!

Обратите внимание на отличия в запуске приложений в строке терминала Unix и Windows. Если всё будет в порядке (вы не сделаете опечаток, не будет проблем в кодировках, шрифтах или ещё с чем-нибудь ещё ("не удаётся найти указанный файл", "stream did not contain valid UTF-8", ..)) - будет напечатана строка Hello, Rust world! Привет, Мир!. Поздравляю! Вы написали первую программу на Rust! Добро пожаловать в увлекательное путешествие в мир Rust! :-)

Если вам уже понравилось писать на Rust, создайте файл hello_world.rs введите тот же код и проверьте его работу. Далее создайте i_like_hello_world.rs и проделайте тоже самое. Если программы будут работать корректно - будет печататься тот же текст - вы закрепите свои первые навыки и почувствуете уверенность. Желаю успеха!

Как это работает

Теперь, давайте разберёмся, как же работает ваша новая "Hello Rust world!"-программа. Первым делом, разберем исходный код:

fn main() {

}

Этот текст определяет Rust-функцию. Функция main - особенная. Это т.н. точка входа в программу (если читатель уже знаком с C, C++, Java, то тут тоже самое). main - начало всех начал вашей программы. Т.е. этот текст значит следующее: "Объявление функции с именем main, у которой нет параметров и она ничего не возвращает во внешнюю среду." Если же у неё были бы параметры, они были бы заключены в круглые скобки ( ).

Также обратите внимание, что содержимое функции обрамляется фигурными скобками. { } (также как и в Си, С++, Java, Go). Эти скобки обязательны для описания Rust-функций. Для повышения читаемости кода (опять же, по соглашению) рекомендуем размещать открывающую фигурную скобку { на той же строке, что и наименование функции и описание её параметров, и отделять её одним пробелом. Вот так: fn main() {.

Содержимое функции main:


# #![allow(unused_variables)]
#fn main() {
    println!("Hello, Rust world! Привет, Мир!");
#}

Эта строчка кода описывает все действия программы: печать текста в терминальной строке. Есть определённые стилистические уточнения. Первое, в стиле написания кода на Rust используется четыре пробела вместо символа табуляции.

Второе, это println! (называется Rust макрос). Таким образом, в Rust реализуется метапрограммирование. Обратите, пожалуйста, внимание, что в конце слова println стоит знак !. Именно этот знак говорит о том, что это макрос, а не функция. Это важно! Пожалуйста, обратите на это внимание!

Далее, в круглых скобках находится текст "Hello, Rust world! Привет, Мир!". Он имеет тип данных строка. Этот текст передаётся макросу println! как входные данные. Далее, макрос выполняет печать данного текста в терминале. Всё достаточно просто и понятно, не так ли?!

Строка заканчивается символом ;. Этот символ информирует о том, что выражение окончено, далее можно напечатать следующее.


# #![allow(unused_variables)]
#fn main() {
    println!("Hello, Rust world! Привет, Мир!");
    println!("Hello! Привет!");
#}

Пожалуйста, добавьте новую строчку кода в вашу программу и проделайте необходимые операции, для того чтобы увидеть новый текст в терминале!

Unix:

$ rustc main.rs
$ ./main
Hello, Rust world! Привет, Мир!
Hello! Привет!

Windows CMD:

> rustc main.rs
> main
Hello, Rust world! Привет, Мир!
Hello! Привет!

Компиляция и выполнения - это два различных этапа работы

В разделе "Написание и запуск первой программы" было показано, как выполнить созданную программу. Сейчас мы постараемся объяснить как это работает.

Прежде чем выполнить программму (т.е. вызвать в строке териминала исполняемый файл программы main), необходимо скомпилировать её с помощью программы-компилятора rustc.

Unix:

$ rustc main.rs

Аналогично на Windows.

Windows CMD:

> rustc main.rs

Таким же образом компилируются программы на C, C++, Java, Go. Результатом компиляции Rust-программы является бинарный файл. Его можно увидеть в списке файлов:

Unix:

$ ls
main  main.rs

Windows:

> dir /B
main.exe
main.rs

Опция /B позволяет отображать только файлы.

В списке присутствуют два файла: файл с исходным кодом программы .rs и бинарный файл (main.exe в Windows, main на других операционных системах).

$ ./main  #или .\main.exe на Windows

Если бы в исходном коде main.rs в входных параметрах макроса был введён текст “Hello, world!”, то в строке терминала вы бы увидели Hello, world!.

Пожалуйста, отредактируйте файл исходного кода и проверьте работу нового бинарного файла!

Если у Вас есть опыт программирования на динамически компилируемых языках, таких как Ruby, Python или JavaScript то вас, наверное, удивит необходимость разделения компиляции и выполнения программ. В составе утилит Rust есть Ahead-of-Time (AOT) компилятор. Он позволяет создавать автономные бинарные файлы, которые позже могут быть использованы там, где нет Rust утилит. Это весьма удобно, как для разработчиков программного обеспечения, так и для пользователей. Динамически компилирумые программы лишены такой возможности. Как бы там ни было, использование и динамически, и статически компилируемых языков программирования имеет свои плюсы и минусы.

Компиляция простых программ с помощью rustc - это замечательно, но если ваш проект начнёт разрастаться всё больше и больше, вам понадобятся средства управления всеми компонентами, а также удобные возможности всего цикла командной разработки. Далее, мы представим вам программу cargo, которая поможет создать удобную среду разработки.

Привет, Cargo!

Cargo - это система управления пакетами для разработки программ на Rust. Удобство её использования оценили программисты Rust. Cargo позволяет упростить процесс разработки. Например, Сargo поможет скомпилировать программу из исходного кода, скачать требуемые библиотеки для вашего проекта. В терминах языка программирования Rust внешние библиотеки называются зависимостями (dependencies).

Такая простая программа, которую мы написали (main) не имеет зависимостей. Поэтому Cargo нам может понадобиться только лишь для компиляции. Если же Вы напишете (когда-нибудь напишете) более сложную программу на Rust, то весь функционал Cargo, который помогает добавить зависимости в инфраструктуру вашего проекта будет весьма кстати.

Поскольку огромное (подчёркиваю огромное) количество Rust-проектов использует Cargo мы предположим, что и вам его функционал придётся по вкусу и будет удобен. Всё что вам нужно для его использования идёт в комплекте с компилятором и другими утилитами. Для того, чтобы проверить правильно ли установлен Cargo, введите в терминал следующую команду:

$ cargo --version
cargo 0.21.0

Если будет выведена версия программы - Cargo скорее всего работает. А если программа cargo не найдена - пожалуйста, постарайтесь устранить проблемы установки (возможно, вам понадобиться установить Cargo с помощью различных способов, которые приведены на сайте doc.crates.io.

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

$ cargo --help

Создание Cargo проекта

Создадим проект используя Cargo! Думаю, что он будет отличаться от того проекта, что был создан нами ранее (проект main):

Unix:

$ cd ~/projects

Windows:

> cd %USERPROFILE%\projects

Далее введём следующую команду:

$ cargo new hello_cargo --bin
$ cd hello_cargo

Мы устанавливаем аргумент --bin для cargo-команды new для того, чтобы по шаблону была создана структура приложения командной строки - консольный проект (бинарное приложение). hello_cargo - это название нашего нового Rust проекта.

Если мы просмотрим список созданных файлов, то мы увидим что внутри папки проекта были созданы файлы и папка: Cargo.toml,.gitignore, src. Внутри папки src находится файл main.rs. По умолчанию Cargo оснастил папку нашего проекта файлом .gitignore - это служебный файл git хранилища. Вы можете создать проект и без инициализации git хранилища (или какого-либо иного вида системы контроля версий файлов), если воспользуетесь флагом --vcs при создании нового проекта:

$ cargo new hello_cargo2 --bin --vcs none
$ cd hello_cargo2

Содержимое файла Cargo.toml:

Filename: Cargo.toml

[package]
name = "hello_cargo"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]

[dependencies]

Это файл формата TOML (Tom’s Obvious, Minimal Language). Формат TOML очень похож на INI, однако имеет свои специфические особенности.

На первой строке расположен заголовок секции[package]. Далее следует описание пакета. По мере усложнения проекта, в данный файл будут добавлять другие секции.

Следующие строки - это строки описания пакета: имя, версия, авторы. Если в строке у Вас написано имя компьютера, значит Вы ещё не до конца настроили систему для работы с Cargo (Cargo не может считать информацию о вас, а также недоступны другие опции работы с github). Информацию об авторстве Cargo берёт из параметров вашего git вашей учетной записи. Подробнее с этим можно ознакомиться перейдя по ссылке. Если вы хотите устранить этот недостаток, то для пользователей Windows предлагаю следующее решение:

  1. Устанавливаете "GitHub Desktop".
  2. В приложении подключаетесь к своей учётной записи.
  3. Далее создаёте проект.
  4. Открывайте файл Cargo.toml созданного проекта и видите имя учётной записи и e-mail в строке authors.

Последняя строка [dependencies] - это заголовок секции crates (так называются пакеты в терминах языка программирования Rust). Этот список содержит описание зависимостей вашего проекта и предоставляет Cargo необходимую информацию для загрузки и компиляции. Т.к. в нашем шаблонном проекте не используются внешние зависимости - эта секция пуста. В проекте "Угадай число" мы заполним информацию о зависимостях.

Если у Вас появились вопросы, пожалуйста, ознакомьтесь с разделом FAQ документации.

Теперь перейдём к рассмотрению содержимого файла src/main.rs:

Filename: src/main.rs

fn main() {
    println!("Hello, world!");
}

Как видите, тут уже есть код программы, который мы писали ранее. Отличия нашего предыдущего проекта от сгенерированного Cargo следующие:

  • Имеется специальная папка для исходного кода программы src.
  • Есть файл конфигурации Cargo.toml

Программисты на Java, возможно, обратят внимание на концептуальную схожесть содержания проекта с Maven. Да, есть что-то общее. Интуиция вас не подводит. В корневой папке проекта также могут содержаться файлы README, CONTRIBUTING, LICENSE, файлы конфигурации, а также всё что угодно, не относящееся к исходному коду программы. Концепция структуры хранения данных Cargo позволяет унифицировать структуру Rust проектов, что делает их понятными для изучения и развития. Такие проекты потенциально могут быть сколько угодно сложными и ёмкими. Cargo поможет со всеми ими справиться.

Если в своей работе вам придётся столкнуться с необходимостью конвертации проекта Rust в Cargo-проект - генерируемые шаблоны помогут вам.

Структура Cargo-проекта:

.
├── Cargo.lock
├── Cargo.toml
├── benches
│   └── large-input.rs
├── examples
│   └── simple.rs
├── src
│   ├── bin
│   │   └── another_executable.rs
│   ├── lib.rs
│   └── main.rs
└── tests
    └── some-integration-tests.rs

Более подробную информацию о структуре типового Cargo-проекта вы можете узнать на сайте;

Сборка и запуск Cargo проектов

В чём же разница между сборкой и запуском Cargo проекта. Предлагаю разобраться! Для этого в папке созданного проекта введём следующую cargo-команду - build (build - это cargo-команда для компиляции текущего проекта. С работой команды new вы уже знакомы):

$ cargo build
   Compiling hello_cargo v0.1.0 (file:///projects/hello_cargo)

Результат - создание бинарного файла в папке target/debug/hello_cargo (target\debug\hello_cargo.exe в Windows). Проверим работу созданного файла:

Unix:

$ ./target/debug/hello_cargo
Hello, world!

Windows:

> .\target\debug\hello_cargo
Hello, world!
> # or .\target\debug\hello_cargo.exe
> # or target\debug\hello_cargo.exe
> # or target\debug\hello_cargo

Отлично! Всё работает замечательно. :-)

Обратите внимание, что команда cargo build при первом её запуске в текущем проекте создаёт файл Cargo.lock. Этот файл содержит следующий текст:

Filename: Cargo.lock

[root]
name = "hello_cargo"
version = "0.1.0"

Файл Cargo.lock необходим для отслеживания зависимостей вашего проекта. Так как текущей проект не содержит зависимостей, данный файл не содержит данных для отслеживания зависимостей. По мере усложнения проекта Cargo будет отслеживать зависимости (данный файл будет заполняться необходимой для этого информацией). Более подробно об этом можно узнать из документации проекта.

Для компиляции и последующего запуска программы на выполнение воспользуйтесь Cargo-командой run:

$ cargo run
     Running `target/debug/hello_cargo`
Hello, world!

Обратите внимание, что в командной строке не было напечатано ничего о компиляции. Cargo умеет отслеживать состояние исходных файлов проекта. Так как изменений не было - следовательно, перекомпиляция не нужна. Пожалуйста, внесите в исходный код Rust-файла изменение. Скопируйте строчку кода ввода на печать и вставьте её на новую строку. Сохраните ваши изменения. Запустите команду Cargo-команду run ещё раз. Обратите внимание, что программа будет перекомпилирована и запущена.

$ cargo run
   Compiling hello_cargo v0.1.0 (file:///projects/hello_cargo)
     Running `target/debug/hello_cargo`
Hello, world!

Если запустить Cargo-команду run ещё раз - перекомпиляции не случится.

$ cargo run
   Compiling hello_cargo v0.1.0 (file:///projects/hello_cargo)
     Running `target/debug/hello_cargo`
Hello, world!
$ cargo run 
     Running `target/debug/hello_cargo`
Hello, world!

Подведём итоги:

  • Для создания сложных проектов вместо того, чтобы использовать Rust-компилятор rustc непосредственно, наилучшим решением является использование Cargo-команд.
  • Cargo-структура проекта использует принцип разделения и систематизации, благодаря которому возможно построение проектов со сложными внешними зависимостями.

Кроме того, использование Cargo-команд позволяет вести многоплатформенную разработку. Более подробно о проекте Cargo и его возможностях можно узнать из документации:

$ cargo --help

А также из материалов сайта.

Сборка готовых (оптимизированных) Rust-приложений

Когда проект уже готов к выпуску, можно воспользоваться Cargo-командой build c флагом --release:

$ cargo build --release

Эта команда скомпилирует и оптимизирует вашу программу. В папке target/release будет создан бинарный файл. Все оптимизации позволят программе работать быстрее. Обратная сторона подобной операции - более длительное время компиляции. Поэтому существуют две команды компиляции - для разработки и для финальных версий. Для проверки производительности ваших Rust-программ мы рекомендуем использовать бинарные файлы, которые были получены путем компиляции и оптимизации (которые сохраняются в папке target/release).

Cargo-конвенции

Конечно, простые примеры не могут раскрыть всех возможностей, которые предоставляет Cargo по сравнению с прямой работой с компилятором. Для более сложных проектов концепции Cargo - это проверенный временем стиль разработки. Привыкайте к хорошему стилю работы даже с простыми проектами!

Этапы начала работы с любым Rust-проектом:

$ git clone someurl.com/someproject
$ cd someproject
$ cargo build

Для того, чтобы почувствовать, что такое работа с "неигрушечным" проектом, проделайте эти операции на примере проекта Cargo. Ознакомьтесь с содержимым файлов Cargo.toml, Cargo.lock, посмотрите содержимое папки src.

Проект Cargo находится по адресу: github.com/rust-lang/cargo. Также вы можете попрактиковаться и над другими проектами, исходные коды которых хранятся на github.com. Для их поиска используйте возможности расширенного поиска проектов.

Если вы хотите подробнее ознакомиться с работой проектов Cargo, со всем, что мы не рассказали в этой вводной статье, пожалуйста, ознакомьтесь с официальным руководством.