[Программирование, Rust, Читальный зал] Как я влюбился в Rust и чего мне это стоило
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Языков программирования на текущий момент существует превеликое множество. Одни безусловно хороши для определенных целей, другие признаются универсальными и используются многими для решения повседневных задач.
В этой статье я хочу рассказать, как и почему Rust стал для меня основным и любимым языком для решения персональных задач самого разнообразного профиля, и что именно доставляет мне особенное удовольствие при его использовании.
Хочу сразу заметить, что эта статья целиком и полностью — субъективное мнение автора, единственная цель которой — заинтересовать читателей, ценящих в программировании как хобби те же самые вещи, что и он сам, и речь в ней не пойдёт ни о быстродействии, ни о востребованности языка в сфере IT, ни о каких-либо других технических составляющих этой области, вокруг которой часто возникают разного рода споры. Я остановлюсь на том, что Rust — быстрый и безопасный компилируемый ЯП общего назначения. Об остальном — далее.
Какой язык я искал
Лично я в первую очередь делю все ЯП на две большие группы: интерпретируемые и компилируемые. Для личных проектов (разумеется, крупнее скриптов автоматизации) я искал именно второй, так как ключевой для меня была возможность переносить исполняемые файлы на внешних и облачных дисках и запускать их на офисных ПК без каких-либо проблем.
Важным условием при выборе также была возможность без трудностей скомпилировать исполняемые файлы под Windows, Mac OS и дистрибутивы Linux, так как рабочих машин у меня несколько, а запускаться и работать код должен на каждой. Некоторые из проектов шли даже под Raspberry Pi, где мне вдобавок требовалось бережное отношение к памяти. Ну и напоследок я искал простоту в использовании (не в написании кода): чтобы библиотеки ставились (и писались) самым очевидным и удобным образом, чтобы структура проектной директории была простой и понятной, а общение с компилятором – приятным и безболезненным. За ковидный карантин я успел перепробовать множество разных языков, остановившись в итоге на Расте. Давайте узнаем, почему.
Путь к "Hello World"
Так как, пожалуй, большинство читателей ранее с этим языком не взаимодействовали, я начну с самого начала: процесса первого знакомства. В процессе поиска своего идеального ЯП, очень часто я сталкивался с трудностями уже на этом этапе. Где-то были определенные сложности в выборе и настройке IDE, где-то установка или использование компилятора требовало множества разных манипуляций, которые сходу отпугивали и отбивали желание работать. Давайте взглянем, что предстоит пройти человеку, решившему с нуля написать на Расте простейший "Hello World".
Для начала загрузим rustup – программу, которая установит и будет поддерживать в актуальном состоянии все необходимое для написания программ. На Unix-подобных ОС сделать это можно одной командой:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
Дополнительные инструкции по установке, а также версия для Windows доступны на официальном сайте.
После установки утилиты проверять обновления системы можно командой rustup update. В остальном для работы сама утилита нам больше не понадобится, ведь все остальные манипуляции мы будем проводить с системой сборки и пакетным менеджером системы – cargo.
Для начала проверим версию и убедимся, что все встало как надо, выполнив команду cargo --version.
Порядок? Идём дальше.
Сделаем cd в папку, где храним все проекты и попросим cargo создать новый командой cargo new hello-rust.
В папке будет создана новая директория со всеми необходимыми файлами:
hello-rust
|- Cargo.toml
|- src
|- main.rs
Cargo.toml здесь – файл манифеста, в котором хранятся все метаданные проекта. Подробнее о нем – чуть позже.
src/main.rs – не трудно догадаться, файл с исходным кодом нашего проекта. Сразу после создания проекта в нем появляется код, выводящий в терминал Hello, world!.
Можно, даже не открывая его, скомандовать cargo run и получить желаемое.
$ cargo run
Compiling hello-rust v0.1.0 (/Users/ag_dubs/rust/hello-rust)
Finished dev [unoptimized + debuginfo] target(s) in 1.34s
Running `target/debug/hello-rust`
Hello, world!
Вот и все. Для меня впервые путь к "Hello World" оказался невероятно дружелюбным и простым.
Но, разумеется, выводом текста в консоль никто ограничиваться не будет. Следующий шаг – учиться, учиться, и еще раз учиться.
Взглянуть целиком на официальный Quick Start Guide можно здесь
Приключения на пути к познанию
Ключевым моментом для любого, решившего выучить новый ЯП, будет, разумеется, сам процесс изучения. Вопрос доступности и качества документации и справочных материалов здесь встаёт особенно остро. Давайте узнаем, как с этим обстоят дела у Раста.
Спойлер: обстоят они просто замечательно. Одна лишь официальная документация включает в себя множество самых разнообразных изданий, каждое из которых проработано самым детальным образом.
Вот лишь малая часть информации, доступная на официальном сайте:
- "Книга" Rust — полное руководство языка, изучив которое с нуля, можно добиться вполне уверенного понимания базовых и продвинутых элементов
- Rust by Example – собрание множества примеров практического применения языка для решения разных задач с комментариями и упражнениями
- Rustlings – консольная программа, помогающая первопроходцам освоиться с синтаксисом и основными понятиями Rust
- Reference и Rustonomicon – справочные материалы для продвинутых пользователей, желающих отточить своё мастерство и познать самые тёмные уголки программирования на Расте
- Embedded Book – руководство по использованию языка на микроконтроллерах и другом чистом железе
- Rustdoc – справка по документированию проектов и библиотек
- Cargo Book – материалы для работы с системой сборки проектов
Вместе с самим языком документация постоянно обновляется и дополняется, а вкупе с множеством форумов и вовсе даёт абсолютно исчерпывающую информацию об использовании. Лично у меня путь от первого знакомства до свободного написания сложных программ и библиотек занял месяц. Много это или мало – судите сами.
Когда знаний и опыта наконец достаточно, самое время написать что-нибудь интересное. Следующее, за что я собираюсь хвалить Раст —
Синтаксис и возможности
Нельзя отрицать, что код на Rust не самый читабельный и очевидный для непосвященного пользователя. Нельзя также не заметить, что вместе с пониманием устройства всех деталей синтаксиса приходит и любовь к нему.
База
Во первых – точки с запятой и фигурные скобки. Да, многие на дух такое не переносят, считая пережитком прошлого. Я немного другого мнения: при работе с большими объемами кода, который временами приходится кардинально менять, скобки – спасение, а точки с запятой позволяют мне при особо острой необходимости писать последовательности команд одной строкой.
Ставить их везде, кстати, вовсе не обязательно:
if my_string.chars().count() == 3 {
println!("В строке три знака"); //Здесь точка с запятой нужна в любом случае
std::process::exit(-1) //А тут ее можно опустить, компилятору достаточно закрывающейся скобки
}
Во вторых – функции. Выглядят они в Расте так:
// Объявление функций всегда производится ключевым словом 'fn'. Тип возвращаемого значения (при наличии), указывается с помощью 'стрелочки'
fn is_divisible_by(lhs: u32, rhs: u32) -> bool {
// На ноль делить нельзя
if rhs == 0 {
return false
}
// Это выражение, результат которого – bool. Ключевое слово 'return' здесь необязательно, так как этот результат не перехватывается до выхода из функции
lhs % rhs == 0
}
Лично я – ярый сторонник именно такого вида записи, встречающегося и в других языках. Решение, принятое, например, в C++ или C# (с указанием типа возвращаемого значения вместо ключевого слова fn), на мой взгляд, куда менее очевидно, особенно если приходится иметь дело со сложными типами.
Далее вкратце перечислю мои самые любимые сахара:
Удобоваримый вид импорта модулей
Импорт библиотек реализован здесь максимально кратким и эффективным образом, без лишних ключевых слов и с удобным наследованием:
use std::fs импортирует только модуль fs из std,
use std::io::{Write, Read} возьмет структуры Write и Read из предыдущего,
use std::{io, fs::File, time::*} импортирует модуль io из std, структуру File модуля fs из std и все вложенные в модуль time из std модули и структуры. Одной строкой.
В крупных проектах с десятками зависимостей в одном файле такие возможности – просто спасение.
Атрибутные макросы
Написание кода, который должен выполниться до сборки программы (к примеру установка порядка условной компиляции), реализовано здесь крайне простым образом.
Так, например, всего в одну строку можно задать включение определенного модуля лишь в сборку под MacOS: #[cfg(target_os = "macos")]. Идентичный этому синтаксис у всех подобных макросов: #[derive(PartialEq, Eq)], #[post("/user", data = "<new_user>")], #[test] и так далее. Лично мною такое решение воспринимается куда охотнее, чем аналогичные решения в тех же крестах.
Match
Match в Расте – продвинутая версия знакомого многим switch/case. Давайте взглянем, на что он способен:
let my_age = 13;
match my_age {
// Проверка точного значения
1 => println!("Вам годик"),
// Проверка нескольких значений
2 | 3 | 5 | 7 | 11 => println!("Ваш возраст – простое число"),
// Проверка интервала (включительно)
13..=19 => println!("Вы подросток"),
// Проверка интервала (включительно) с выводом значения
n @ 80..=100 => println!("Вы старый дед {}и лет", n),
// Проверка вообще на что угодно с помощью if
i if i % 2 > 3 => println!("Остаток от деления вашего возраста на два больше трех"),
// Работа с остальными случаями
_ => println!("Вы кто")
}
Мощная и удобная штука, которую я использую практически в каждом проекте.
Пара слов об обработке ошибок
Принципы обработки ошибок, конечно, имеют весьма посредственное отношение к синтаксису, но затронуть я хочу их максимально поверхностно, чтобы не нагружать читателя лишней информацией, а лишь продемонстрировать базовые принципы этого процесса.
Error handling – это очень важно. Когда я пишу проект, что должен как можно дольше оставаться в поднятом состоянии и восстанавливаться от любых возможных ошибок, я хочу быть уверенным, что обработал 100% их всех. В этом мне помогает, на мой взгляд, одна из самых важных особенностей языка, ведь я всегда знаю, в каком месте может возникнуть ошибка.
Особенность эта – обработка ошибок, основанная на результате каждой опасной операции, а не на исключениях.
Для этого в языке предусмотрено два основных типа: Option и Result. Для начала – о первом.
// Объявим функцию, параметром которой будет опциональный тип `Option`, возвращающий (при наличии) `&str`.
fn give_guest(gift: Option<&str>) {
// 'Option' оборачивается в два значения: 'Some' и 'None'. Первый означает, что некий объект содержит в себе необходимые данные, второй, соответсвенно, что в нем ничего нет
// Проверим, что содержится внутри переменной 'gift', с помощью уже знакомого нам оператора 'match'
match gift {
Some("торт") => println!("Спасибо за торт"),
Some(inner) => println!("{}? Как мило.", inner),
None => println!("Нет подарка? Ну что ж."),
}
}
give_guest(Some("Наручные часы")); // Напечатает в консоль 'Наручные часы? Как мило.'
give_guest(None) // Напечатает 'Нет подарка? Ну что ж.'
Вместо массивного решения с match можно использовать символ ?, чтобы выполнять код только в случае, когда необходимое значение есть:
fn next_birthday(current_age: Option<u8>) -> Option<String> {
// Если 'current_age' – 'None', данная функция вернёт 'None'
// Если 'current_age' – 'Some', внутренняя 'u8' получает значение 'next_age'
let next_age: u8 = current_age?;
Some(format!("В следующем году мне исполнится {}", next_age))
}
next_birthday(Some(8)); // Вернёт 'Some("В следующем году мне исполнится 8")'
next_birthday(None); // Вернёт 'None'
Похожее решение реализовано, например, в языке Swift, где вместо Some напрямую передаётся значение, а заменой None служит кейворд nil.
Option возвращается функциями, выполнение которых не всегда означает получение результата. А так как Раст не даст мне незаметно взять возвращённый функцией результат, заставив меня либо обработать и Some, и None, либо развернуть результат с помощью unwrap(), что вызовет невосстанавливаемое исключение (панику, подробнее о ней чуть позже), я гарантированно получаю уверенность в отсутствии неожиданных "вылетов" своей программы из-за отсутствия чего-либо, что должно быть, и чего нет.
Result работает аналогично, но используется именно для обработки ошибок, возникших во время выполнения кода.
// Например, мы пытаемся распарсить строку в число. Результатом встроенной функции 'parse' может быть либо тип 'Ok', содержащий числовой результат, либо 'Err', содержащий информацию об ошибке
fn print_num(string_number: &str) {
match string_number.parse::<i32>() {
Ok(number) => println!("Ваше число – {}", number),
Err(e) => println!("Ошибка: {}", e)
}
}
print_num("8") // Вернёт 'Ваше число – 8'
print_num("n") // Вернёт 'Ошибка: invalid digit found in string'
В случае, если в успешном выполнении кода или получении искомого результата мы уверены на все сто, Option и Result могут быть развернуты:
string_number.parse::<i32>().unwrap(); // В случае, если 'string_number' не является числом или превышает указанную разрядность, будет вызвана паника и программа прекратит выполнение:
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: ParseIntError { kind: InvalidDigit }'
Панику можно вызвать самостоятельно:
fn drink(beverage: &str) {
// Представим, что у нас паническая боязнь лимонада
if beverage == "лимонад" { panic!("AAAaaaaa!!!!") }
println!("Ура, {}, я как раз хотел пить", beverage);
}
fn main() {
drink("вода"); // Напечатает 'Ура, вода, я как раз хотел пить'
drink("лимонад") // Паника: thread 'main' panicked at 'AAAaaaaa!!!!'
}
Таким образом код получается крайне безопасным, что дает мне лишнюю толику спокойствия.
Возвращаясь к обсуждению синтаксиса: вместо while true Rust поддерживает одно ключевое слово loop, что, почему-то, особенно меня умиляет.
Замечательно, код написан и мы им довольны. Давайте подробнее посмотрим на cargo, утилиту, которая помогала нам в этом непростом процессе.
Последнее слово о cargo
При работе с Растом вам в принципе не нужна ни IDE, ни даже продвинутый редактор кода. Все необходимые манипуляции с кодом, включая линт чек, сборку, публикацию и загрузку внешних модулей выполняет CLI утилита cargo. Взглянем, как это выглядит на практике.
- С помощью cargo new мы создали новый проект
- cargo build или cargo run собирает и запускает наш код соответственно
- cargo publish публикует проект на официальном регистре пакетов Rust
Но как добавить в проект зависимость? Очень просто. В этом нам поможет Cargo.toml – упомянутый ранее файл манифеста, автоматически созданный cargo вместе с нашим проектом.
Ознакомимся с его содержанием:
[package] # Здесь содержится основная информация о проекте
name = "hide"
version = "0.1.5"
authors = ["Otter18 <otter18@somemail.ru>"]
edition = "2018"
[dependencies] # А здесь – все необходимые зависимости с фиксированными версиями
directories = "3.0.1"
progress_bar = "0.1.3"
Процесс поиска и добавления модулей реализован здесь необыкновенно просто:
- Находим нужный модуль на crates.io
- Вставляем строчку с именем и версией в файл манифеста
- Все. cargo сам скачает, установит и подключит зависимость при первой сборке проекта.
Эта всемогущая утилита также обладает невероятно детальным выводом сообщений об ошибках и предупреждений, возникших во время сборки,
как при работе из командной строки:
error: cannot find macro 'pritln' in this scope
--> src/main.rs:2:5
|
2 | pritln!("Hello, world!");
| ^^^^^^ help: a macro with a similar name exists: 'println'
error: aborting due to previous error
так и с помощью множества официальных плагинов для разных редакторов кода:
Подводим итоги
Вот этим и покорил меня Rust. Невероятным вниманием к деталям, очевидностью процесса сборки и работы с модулями, широкой экосистемой, любопытным синтаксисом и обилием справочных материалов. Он упорядочил работу над моими проектами, поставив ее на поток.
В качестве примера хочу поделиться одним из своих открытых проектов — программой, шифрующей файлы с помощью симметричного алгоритма AES по двум ключам. Я написал ее в попытке создать наиболее простое и легкое кроссплатформенное решение для скрытия своих данных от посторонних глаз. Она поддерживает фильтрацию файлов по размерам, типам, именам, индексам и интервалам и работает с вложенными папками.
Теперь мои планы на будущее – ещё больше погрузиться в изучение этого языка, познав самые тёмные его уголки.
Если у вас был похожий приятный опыт, но касательно другого ЯП, расскажите мне об этом в комментариях, мне будет жутко интересно почитать.
Спасибо за внимание, надеюсь, сегодня вы узнали для себя что-то новое.
Облачные серверы от Маклауд отлично подходят за разработки под Rust.
Зарегистрируйтесь по ссылке выше или кликнув на баннер и получите 10% скидку на первый месяц аренды сервера любой конфигурации!
оригинал
===========
Источник:
habr.com
===========
Похожие новости:
- [Читальный зал, Научно-популярное, Физика] Гравитация как двойная копия остальных взаимодействий (перевод)
- [Open source, Программирование, Управление разработкой, Управление проектами] Открываем доступ к Platform V — опенсорсному суперфреймворку Сбера
- [Разработка веб-сайтов, JavaScript, TypeScript] Практическое руководство по TypeScript для разработчиков (перевод)
- [Python, Программирование, Машинное обучение] Выбираем метод для поиска схожих операций
- [Разработка веб-сайтов, CSS, Совершенный код] Трюки CSS, которые сделают из вас ниндзя верстки
- [Программирование, Дизайн] Скринкасты для разработчиков: новый формат на Техностриме от Mail.ru Group
- [Научно-популярное, Биотехнологии, Здоровье] Ахиллесова пята коронавируса (перевод)
- [Ненормальное программирование, Assembler, Игры и игровые приставки] Перепрограммирование GameBoy за счёт бага в Pokemon Yellow (перевод)
- Уязвимости в Please, альтернативе sudo, написанной на языке Rust
- [Занимательные задачки, Программирование, Управление персоналом, Читальный зал, Мозг] Помогите Снежинке стать программистом
Теги для поиска: #_programmirovanie (Программирование), #_rust, #_chitalnyj_zal (Читальный зал), #_rust, #_opyt_ispolzovanija (опыт использования), #_jazyk_programmirovanija_rust (язык программирования rust), #_programmirovanie (программирование), #_lichnyj_opyt (личный опыт), #_blog_kompanii_maklaud (
Блог компании Маклауд
), #_programmirovanie (
Программирование
), #_rust, #_chitalnyj_zal (
Читальный зал
)
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 18:28
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Языков программирования на текущий момент существует превеликое множество. Одни безусловно хороши для определенных целей, другие признаются универсальными и используются многими для решения повседневных задач. В этой статье я хочу рассказать, как и почему Rust стал для меня основным и любимым языком для решения персональных задач самого разнообразного профиля, и что именно доставляет мне особенное удовольствие при его использовании. Хочу сразу заметить, что эта статья целиком и полностью — субъективное мнение автора, единственная цель которой — заинтересовать читателей, ценящих в программировании как хобби те же самые вещи, что и он сам, и речь в ней не пойдёт ни о быстродействии, ни о востребованности языка в сфере IT, ни о каких-либо других технических составляющих этой области, вокруг которой часто возникают разного рода споры. Я остановлюсь на том, что Rust — быстрый и безопасный компилируемый ЯП общего назначения. Об остальном — далее. Какой язык я искал Лично я в первую очередь делю все ЯП на две большие группы: интерпретируемые и компилируемые. Для личных проектов (разумеется, крупнее скриптов автоматизации) я искал именно второй, так как ключевой для меня была возможность переносить исполняемые файлы на внешних и облачных дисках и запускать их на офисных ПК без каких-либо проблем. Важным условием при выборе также была возможность без трудностей скомпилировать исполняемые файлы под Windows, Mac OS и дистрибутивы Linux, так как рабочих машин у меня несколько, а запускаться и работать код должен на каждой. Некоторые из проектов шли даже под Raspberry Pi, где мне вдобавок требовалось бережное отношение к памяти. Ну и напоследок я искал простоту в использовании (не в написании кода): чтобы библиотеки ставились (и писались) самым очевидным и удобным образом, чтобы структура проектной директории была простой и понятной, а общение с компилятором – приятным и безболезненным. За ковидный карантин я успел перепробовать множество разных языков, остановившись в итоге на Расте. Давайте узнаем, почему. Путь к "Hello World" Так как, пожалуй, большинство читателей ранее с этим языком не взаимодействовали, я начну с самого начала: процесса первого знакомства. В процессе поиска своего идеального ЯП, очень часто я сталкивался с трудностями уже на этом этапе. Где-то были определенные сложности в выборе и настройке IDE, где-то установка или использование компилятора требовало множества разных манипуляций, которые сходу отпугивали и отбивали желание работать. Давайте взглянем, что предстоит пройти человеку, решившему с нуля написать на Расте простейший "Hello World". Для начала загрузим rustup – программу, которая установит и будет поддерживать в актуальном состоянии все необходимое для написания программ. На Unix-подобных ОС сделать это можно одной командой: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
Дополнительные инструкции по установке, а также версия для Windows доступны на официальном сайте. После установки утилиты проверять обновления системы можно командой rustup update. В остальном для работы сама утилита нам больше не понадобится, ведь все остальные манипуляции мы будем проводить с системой сборки и пакетным менеджером системы – cargo. Для начала проверим версию и убедимся, что все встало как надо, выполнив команду cargo --version. Порядок? Идём дальше. Сделаем cd в папку, где храним все проекты и попросим cargo создать новый командой cargo new hello-rust. В папке будет создана новая директория со всеми необходимыми файлами: hello-rust
|- Cargo.toml |- src |- main.rs Cargo.toml здесь – файл манифеста, в котором хранятся все метаданные проекта. Подробнее о нем – чуть позже. src/main.rs – не трудно догадаться, файл с исходным кодом нашего проекта. Сразу после создания проекта в нем появляется код, выводящий в терминал Hello, world!. Можно, даже не открывая его, скомандовать cargo run и получить желаемое. $ cargo run
Compiling hello-rust v0.1.0 (/Users/ag_dubs/rust/hello-rust) Finished dev [unoptimized + debuginfo] target(s) in 1.34s Running `target/debug/hello-rust` Hello, world! Вот и все. Для меня впервые путь к "Hello World" оказался невероятно дружелюбным и простым. Но, разумеется, выводом текста в консоль никто ограничиваться не будет. Следующий шаг – учиться, учиться, и еще раз учиться. Взглянуть целиком на официальный Quick Start Guide можно здесь Приключения на пути к познанию Ключевым моментом для любого, решившего выучить новый ЯП, будет, разумеется, сам процесс изучения. Вопрос доступности и качества документации и справочных материалов здесь встаёт особенно остро. Давайте узнаем, как с этим обстоят дела у Раста. Спойлер: обстоят они просто замечательно. Одна лишь официальная документация включает в себя множество самых разнообразных изданий, каждое из которых проработано самым детальным образом. Вот лишь малая часть информации, доступная на официальном сайте:
Вместе с самим языком документация постоянно обновляется и дополняется, а вкупе с множеством форумов и вовсе даёт абсолютно исчерпывающую информацию об использовании. Лично у меня путь от первого знакомства до свободного написания сложных программ и библиотек занял месяц. Много это или мало – судите сами. Когда знаний и опыта наконец достаточно, самое время написать что-нибудь интересное. Следующее, за что я собираюсь хвалить Раст — Синтаксис и возможности Нельзя отрицать, что код на Rust не самый читабельный и очевидный для непосвященного пользователя. Нельзя также не заметить, что вместе с пониманием устройства всех деталей синтаксиса приходит и любовь к нему. База Во первых – точки с запятой и фигурные скобки. Да, многие на дух такое не переносят, считая пережитком прошлого. Я немного другого мнения: при работе с большими объемами кода, который временами приходится кардинально менять, скобки – спасение, а точки с запятой позволяют мне при особо острой необходимости писать последовательности команд одной строкой. Ставить их везде, кстати, вовсе не обязательно: if my_string.chars().count() == 3 {
println!("В строке три знака"); //Здесь точка с запятой нужна в любом случае std::process::exit(-1) //А тут ее можно опустить, компилятору достаточно закрывающейся скобки } Во вторых – функции. Выглядят они в Расте так: // Объявление функций всегда производится ключевым словом 'fn'. Тип возвращаемого значения (при наличии), указывается с помощью 'стрелочки'
fn is_divisible_by(lhs: u32, rhs: u32) -> bool { // На ноль делить нельзя if rhs == 0 { return false } // Это выражение, результат которого – bool. Ключевое слово 'return' здесь необязательно, так как этот результат не перехватывается до выхода из функции lhs % rhs == 0 } Лично я – ярый сторонник именно такого вида записи, встречающегося и в других языках. Решение, принятое, например, в C++ или C# (с указанием типа возвращаемого значения вместо ключевого слова fn), на мой взгляд, куда менее очевидно, особенно если приходится иметь дело со сложными типами. Далее вкратце перечислю мои самые любимые сахара: Удобоваримый вид импорта модулей Импорт библиотек реализован здесь максимально кратким и эффективным образом, без лишних ключевых слов и с удобным наследованием: use std::fs импортирует только модуль fs из std, use std::io::{Write, Read} возьмет структуры Write и Read из предыдущего, use std::{io, fs::File, time::*} импортирует модуль io из std, структуру File модуля fs из std и все вложенные в модуль time из std модули и структуры. Одной строкой. В крупных проектах с десятками зависимостей в одном файле такие возможности – просто спасение. Атрибутные макросы Написание кода, который должен выполниться до сборки программы (к примеру установка порядка условной компиляции), реализовано здесь крайне простым образом. Так, например, всего в одну строку можно задать включение определенного модуля лишь в сборку под MacOS: #[cfg(target_os = "macos")]. Идентичный этому синтаксис у всех подобных макросов: #[derive(PartialEq, Eq)], #[post("/user", data = "<new_user>")], #[test] и так далее. Лично мною такое решение воспринимается куда охотнее, чем аналогичные решения в тех же крестах. Match Match в Расте – продвинутая версия знакомого многим switch/case. Давайте взглянем, на что он способен: let my_age = 13;
match my_age { // Проверка точного значения 1 => println!("Вам годик"), // Проверка нескольких значений 2 | 3 | 5 | 7 | 11 => println!("Ваш возраст – простое число"), // Проверка интервала (включительно) 13..=19 => println!("Вы подросток"), // Проверка интервала (включительно) с выводом значения n @ 80..=100 => println!("Вы старый дед {}и лет", n), // Проверка вообще на что угодно с помощью if i if i % 2 > 3 => println!("Остаток от деления вашего возраста на два больше трех"), // Работа с остальными случаями _ => println!("Вы кто") } Мощная и удобная штука, которую я использую практически в каждом проекте. Пара слов об обработке ошибок Принципы обработки ошибок, конечно, имеют весьма посредственное отношение к синтаксису, но затронуть я хочу их максимально поверхностно, чтобы не нагружать читателя лишней информацией, а лишь продемонстрировать базовые принципы этого процесса. Error handling – это очень важно. Когда я пишу проект, что должен как можно дольше оставаться в поднятом состоянии и восстанавливаться от любых возможных ошибок, я хочу быть уверенным, что обработал 100% их всех. В этом мне помогает, на мой взгляд, одна из самых важных особенностей языка, ведь я всегда знаю, в каком месте может возникнуть ошибка. Особенность эта – обработка ошибок, основанная на результате каждой опасной операции, а не на исключениях. Для этого в языке предусмотрено два основных типа: Option и Result. Для начала – о первом. // Объявим функцию, параметром которой будет опциональный тип `Option`, возвращающий (при наличии) `&str`.
fn give_guest(gift: Option<&str>) { // 'Option' оборачивается в два значения: 'Some' и 'None'. Первый означает, что некий объект содержит в себе необходимые данные, второй, соответсвенно, что в нем ничего нет // Проверим, что содержится внутри переменной 'gift', с помощью уже знакомого нам оператора 'match' match gift { Some("торт") => println!("Спасибо за торт"), Some(inner) => println!("{}? Как мило.", inner), None => println!("Нет подарка? Ну что ж."), } } give_guest(Some("Наручные часы")); // Напечатает в консоль 'Наручные часы? Как мило.' give_guest(None) // Напечатает 'Нет подарка? Ну что ж.' Вместо массивного решения с match можно использовать символ ?, чтобы выполнять код только в случае, когда необходимое значение есть: fn next_birthday(current_age: Option<u8>) -> Option<String> {
// Если 'current_age' – 'None', данная функция вернёт 'None' // Если 'current_age' – 'Some', внутренняя 'u8' получает значение 'next_age' let next_age: u8 = current_age?; Some(format!("В следующем году мне исполнится {}", next_age)) } next_birthday(Some(8)); // Вернёт 'Some("В следующем году мне исполнится 8")' next_birthday(None); // Вернёт 'None' Похожее решение реализовано, например, в языке Swift, где вместо Some напрямую передаётся значение, а заменой None служит кейворд nil. Option возвращается функциями, выполнение которых не всегда означает получение результата. А так как Раст не даст мне незаметно взять возвращённый функцией результат, заставив меня либо обработать и Some, и None, либо развернуть результат с помощью unwrap(), что вызовет невосстанавливаемое исключение (панику, подробнее о ней чуть позже), я гарантированно получаю уверенность в отсутствии неожиданных "вылетов" своей программы из-за отсутствия чего-либо, что должно быть, и чего нет. Result работает аналогично, но используется именно для обработки ошибок, возникших во время выполнения кода. // Например, мы пытаемся распарсить строку в число. Результатом встроенной функции 'parse' может быть либо тип 'Ok', содержащий числовой результат, либо 'Err', содержащий информацию об ошибке
fn print_num(string_number: &str) { match string_number.parse::<i32>() { Ok(number) => println!("Ваше число – {}", number), Err(e) => println!("Ошибка: {}", e) } } print_num("8") // Вернёт 'Ваше число – 8' print_num("n") // Вернёт 'Ошибка: invalid digit found in string' В случае, если в успешном выполнении кода или получении искомого результата мы уверены на все сто, Option и Result могут быть развернуты: string_number.parse::<i32>().unwrap(); // В случае, если 'string_number' не является числом или превышает указанную разрядность, будет вызвана паника и программа прекратит выполнение:
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: ParseIntError { kind: InvalidDigit }' Панику можно вызвать самостоятельно: fn drink(beverage: &str) {
// Представим, что у нас паническая боязнь лимонада if beverage == "лимонад" { panic!("AAAaaaaa!!!!") } println!("Ура, {}, я как раз хотел пить", beverage); } fn main() { drink("вода"); // Напечатает 'Ура, вода, я как раз хотел пить' drink("лимонад") // Паника: thread 'main' panicked at 'AAAaaaaa!!!!' } Таким образом код получается крайне безопасным, что дает мне лишнюю толику спокойствия. Возвращаясь к обсуждению синтаксиса: вместо while true Rust поддерживает одно ключевое слово loop, что, почему-то, особенно меня умиляет. Замечательно, код написан и мы им довольны. Давайте подробнее посмотрим на cargo, утилиту, которая помогала нам в этом непростом процессе. Последнее слово о cargo При работе с Растом вам в принципе не нужна ни IDE, ни даже продвинутый редактор кода. Все необходимые манипуляции с кодом, включая линт чек, сборку, публикацию и загрузку внешних модулей выполняет CLI утилита cargo. Взглянем, как это выглядит на практике.
Но как добавить в проект зависимость? Очень просто. В этом нам поможет Cargo.toml – упомянутый ранее файл манифеста, автоматически созданный cargo вместе с нашим проектом. Ознакомимся с его содержанием: [package] # Здесь содержится основная информация о проекте
name = "hide" version = "0.1.5" authors = ["Otter18 <otter18@somemail.ru>"] edition = "2018" [dependencies] # А здесь – все необходимые зависимости с фиксированными версиями directories = "3.0.1" progress_bar = "0.1.3" Процесс поиска и добавления модулей реализован здесь необыкновенно просто:
Эта всемогущая утилита также обладает невероятно детальным выводом сообщений об ошибках и предупреждений, возникших во время сборки, как при работе из командной строки: error: cannot find macro 'pritln' in this scope
--> src/main.rs:2:5 | 2 | pritln!("Hello, world!"); | ^^^^^^ help: a macro with a similar name exists: 'println' error: aborting due to previous error так и с помощью множества официальных плагинов для разных редакторов кода: Подводим итоги Вот этим и покорил меня Rust. Невероятным вниманием к деталям, очевидностью процесса сборки и работы с модулями, широкой экосистемой, любопытным синтаксисом и обилием справочных материалов. Он упорядочил работу над моими проектами, поставив ее на поток. В качестве примера хочу поделиться одним из своих открытых проектов — программой, шифрующей файлы с помощью симметричного алгоритма AES по двум ключам. Я написал ее в попытке создать наиболее простое и легкое кроссплатформенное решение для скрытия своих данных от посторонних глаз. Она поддерживает фильтрацию файлов по размерам, типам, именам, индексам и интервалам и работает с вложенными папками. Теперь мои планы на будущее – ещё больше погрузиться в изучение этого языка, познав самые тёмные его уголки. Если у вас был похожий приятный опыт, но касательно другого ЯП, расскажите мне об этом в комментариях, мне будет жутко интересно почитать. Спасибо за внимание, надеюсь, сегодня вы узнали для себя что-то новое. Облачные серверы от Маклауд отлично подходят за разработки под Rust. Зарегистрируйтесь по ссылке выше или кликнув на баннер и получите 10% скидку на первый месяц аренды сервера любой конфигурации! оригинал =========== Источник: habr.com =========== Похожие новости:
Блог компании Маклауд ), #_programmirovanie ( Программирование ), #_rust, #_chitalnyj_zal ( Читальный зал ) |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 18:28
Часовой пояс: UTC + 5