Создадим парочку простых приложений на популярном языке программирования. Только учитесь работать с Rust? Эта статья для вас!
Как установить Rust на компьютер
Язык Rust сейчас становится все более популярным, и разработчики позаботились, чтобы процесс его установки был максимально простым. На официальном сайте имеется простая инструкция для пользователей GNU/Linux и macOS, следуя которой, можно буквально за один шаг установить Rust на свой компьютер. Нужно просто скопировать и вставить вот эту команду в терминал:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
После вставки команды нажимаем на Enter и ждем, пока скрипт закончит выполняться. В результате в системе появится все необходимое для сборки и выполнения программ, написанных на Rust. Пользователям Windows придется следовать другой инструкции. Для проверки того, что все компоненты установлены как положено, можно воспользоваться такой командой:
cargo --version
Если все в порядке, то вывод должен быть примерно таким:
[alex@alex-pc ~]$ cargo --version cargo 1.80.1 (376290515 2024-07-16) [alex@alex-pc ~]$
Это значит, что Rust и система сборки Cargo были успешно установлены.
Пишем простое консольное приложение
Для создания простейшего консольного приложения нам не потребуется вручную создавать всю структуру файлов и папок. Разработчики и здесь позаботились об удобстве пользователей, создав систему сборки Cargo. С ее помощью можно создавать, собирать и запускать свои проекты.
Но Cargo помимо этого может еще и загружать необходимые для работы программы пакеты (зависимости). Так что это не только система сборки, но еще и менеджер пакетов. Чтобы создать, например, проект под названием hello-rust при помощи Cargo, в терминале вводим такую команду:
cargo new hello-rust
В результате выполнения этой команды в домашней директории должна появиться папка с именем hello-rust, в которой содержатся все файлы проекта. Найдем в ней файл main.rs, расположенный в подпапке src. Он имеет такое содержимое (открыть его можно в любом удобном текстовом редакторе):
fn main() { println!("Hello, world!"); }
Функция main — это точка входа, самая главная функция, с которой начинается выполнение программы. В данном примере вся ее работа заключается в выведении одной-единственной строки «Hello, world!» в консоли. Чтобы запустить программу, надо сначала открыть терминал в папке проекта и ввести в нем следующее:
cargo run
После этого вывод терминала должен быть примерно такой:
[alex@alex-pc hello-rust]$ cargo run Compiling hello-rust v0.1.0 (/home/alex/hello-rust) Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.03s Running `target/debug/hello-rust` Hello, world! [alex@alex-pc hello-rust]$
Как видим, программа успешно запустилась. Теперь давайте рассмотрим более сложную программу. Для примера возьмем игру в угадывание числа. В этой игре компьютер загадывает число, а пользователь пытается его угадать. Новый проект назовем, например, guessing-game. Для создания проекта воспользуемся командой cargo new guessing-game.
Вот так выглядит исходник игры:
use rand::Rng; use std::cmp::Ordering; use std::io; fn main() { println!("Загадано число от 1 до 100. Угадай число!"); let secret_number = rand::thread_rng().gen_range(1..=100); loop { println!("Введите ваше число."); let mut guess = String::new(); io::stdin() .read_line(&mut guess) .expect("Не удалось прочитать строку"); let guess: u32 = match guess.trim().parse() { Ok(num) => num, Err(_) => continue, }; println!("Вы ввели: {guess}"); match guess.cmp(&secret_number) { Ordering::Less => println!("Мое число больше!"), Ordering::Greater => println!("Мое число меньше!"), Ordering::Equal => { println!("Вы угадали!"); break; } } } }
Весь этот исходник нужно поместить в файл main.rs. Но также придется дополнить файл Cargo.toml. В нем надо дописать одну строку для добавления пакета rand в разделе зависимостей (dependencies). После редактирования файл должен выглядеть так:
[package] name = "guessing-game" version = "0.1.0" edition = "2021" [dependencies] rand = "0.9.0-alpha.2"
Пакет rand нужен для генерации случайного числа, которое следует угадывать. Само число после генерации записывается в переменную secret_number. Генерируется число от 1 до 100. Далее идет цикл loop, в котором содержится весь остальной код. Выйти из цикла можно только, если число будет угадано. Программа дает подсказки в процессе угадывания.
Все подсказки прописаны в конструкции match, которая находится в конце цикла. Это оператор выбора, и в нем происходит сравнение загаданного числа с тем числом, которое ввел пользователь. В зависимости от результата сравнения пользователь получает то или иное сообщение. Если числа оказались равны, то выводится сообщение о победе и программа завершает свою работу.
В программе есть еще один оператор match, расположенный в середине цикла. Он нужен для проверки корректности ввода. То есть если пользователь вдруг ввел что-то кроме цифр, то этот оператор не позволит выполняться следующим после себя строкам, а передаст управление в начало цикла. Вследствие этого пользователь снова получает предложение ввести свое число.
Перед запуском сначала можно собрать проект командой cargo build, а потом запустить при помощи cargo run. Первая команда установит все необходимые зависимости и произведет сборку проекта. Но никто не запрещает сразу же скомандовать cargo run. В этом случае все равно будут автоматически подтянуты все зависимости и после всей цепочки компиляций проект будет успешно запущен. Ниже приводится пример работы программы:
[alex@alex-pc guessing-game]$ cargo run Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.05s Running `target/debug/guessing-game` Загадано число от 1 до 100. Угадай число! Введите ваше число. 50 Вы ввели: 50 Мое число меньше! Введите ваше число. 40 Вы ввели: 40 Мое число меньше! Введите ваше число. 30 Вы ввели: 30 Мое число меньше! Введите ваше число. 20 Вы ввели: 20 Мое число больше! Введите ваше число. 25 Вы ввели: 25 Мое число больше! Введите ваше число. 26 Вы ввели: 26 Мое число больше! Введите ваше число. 27 Вы ввели: 27 Вы угадали! [alex@alex-pc guessing-game]$
Далее расскажем, как создавать программы с графическим интерфейсом.
Пишем простое приложение с GUI
Аббревиатура GUI расшифровывается как Graphical User Interface, что в переводе означает графический интерфейс пользователя. По-другому приложения с графическим интерфейсом называют оконными, и, в отличие от консольных, они более привычные и удобные для большинства пользователей.
Для создания приложения с GUI мы будем использовать библиотеку iced. Писать будем простое приложение под названием Counter («Счетчик»), которое хорошо проиллюстрирует принцип создания несложного интерфейса на iced.
Первым делом создаем проект при помощи уже известной команды:
cargo new counter
Далее отредактируем файл Cargo.toml, приведя его в такой вид:
[package] name = "counter" version = "0.1.0" edition = "2021" [dependencies] iced.workspace = true
Последняя строчка показывает, что нужно установить пакет iced в качестве зависимости. Теперь займемся исходником программы. Откроем файл main.rs и вставим в него следующее:
use iced::widget::{button, row, text, Row}; use iced::Center; pub fn main() -> iced::Result { iced::run("Counter", Counter::update, Counter::view) } #[derive(Default)] struct Counter { value: i64, } #[derive(Debug, Clone, Copy)] enum Message { Increment, Decrement, } impl Counter { fn update(&mut self, message: Message) { match message { Message::Increment => { self.value += 1; } Message::Decrement => { self.value -= 1; } } } fn view(&self) -> Row<Message> { row![ button("-").on_press(Message::Decrement), text(self.value).size(50), button("+").on_press(Message::Increment) ] .padding(20) .spacing(10) .align_y(Center) } }
Что мы здесь видим? Функция main содержит всего одну строку. В этой строке вызывается функция run из пакета iced, которая собственно и запускает программу. В качестве параметров этой функции передаются функции update и view. Первая нужна для логики обновления, когда что-либо изменяется в приложении, а вторая описывает графический интерфейс приложения.
Что касается интерфейса, то здесь все просто. В функции view содержится контейнер Row, в котором размещается ряд виджетов: кнопка со знаком минус, за ней текстовая область и еще одна кнопка, но уже со знаком плюс. Контейнер располагает виджеты горизонтально. Последние три строки в функции view — это свойства. примененные к контейнеру. Свойство padding задает отступы по краям, spacing — отступы внутри контейнера (между его секциями), align_y — выравнивание по вертикали.
Взаимодействия между компонентами программы происходят посредством сообщений (message). Мы видим, что для каждой кнопки с помощью функции on_press прописано необходимое действие. При нажатии на плюс число в текстовой области между кнопками должно увеличиться, а при нажатии на минус — уменьшиться.
Нажатия в виде сообщений передаются в функцию update. В этой функции присутствует оператор выбора match, который определяет, какое именно сообщение пришло, и в зависимости от сообщения выполняет то или иное действие (увеличивает или уменьшает переменную value на единицу).
Запустить программу можно также через команду cargo run. Ниже представлены несколько скриншотов запущенной программы:
Задание для самостоятельного выполнения
Попробуйте сделать так, чтобы компоненты интерфейса программы выстраивались по вертикали, а не по горизонтали.
Решение
Для изменения расположения виджетов с горизонтального на вертикальный нужно вместо контейнера Row использовать Column. Также придется заменить свойство align_y на align_x (выравнивание по горизонтали), так как для Column свойство align_y не определено. Еще неплохо бы поменять кнопки местами, чтобы плюс был сверху, а минус снизу. В общем, код должен быть такой:
use iced::widget::{button, column, text, Column}; use iced::Center; pub fn main() -> iced::Result { iced::run("Counter", Counter::update, Counter::view) } #[derive(Default)] struct Counter { value: i64, } #[derive(Debug, Clone, Copy)] enum Message { Increment, Decrement, } impl Counter { fn update(&mut self, message: Message) { match message { Message::Increment => { self.value += 1; } Message::Decrement => { self.value -= 1; } } } fn view(&self) -> Column<Message> { column![ button("+").on_press(Message::Increment), text(self.value).size(50), button("-").on_press(Message::Decrement) ] .padding(20) .spacing(10) .align_x(Center) } }
Внешний вид программы: