Зачем Rust. rustup, cargo и первая программа
открытый урокЭтот раздел читается без входа. Войди, чтобы отмечать прогресс, вести заметки и решать задачи в редакторе. войти
Зачем Rust. rustup, cargo и первая программа
> Привет! Мы открываем раздел про Rust. Этот язык умеет быть быстрым как C, но при этом безопасным. В этом уроке мы вместе установим всё нужное, напишем первую программу и разберём главные фишки Rust. Не переживай: мы будем двигаться постепенно, шаг за шагом, и всё станет понятно.
Идея
Давай начнём с простого вопроса: зачем учить ещё один язык? Ты уже знаком с TypeScript, возможно работал с Go, Haskell или Gleam. Чем же Rust отличается?
Представь, что до 2015 года у программистов был непростой выбор:
- Либо работать как в C: управлять памятью вручную. Это даёт максимальную скорость, но легко допустить ошибку , и программа может «упасть» или стать уязвимой.
- Либо использовать языки со сборщиком мусора (JS, Go, Java): здесь безопаснее, но программа тратит дополнительные ресурсы, а паузы для очистки памяти могут появиться в самый неподходящий момент.
Rust предлагает третий, более умный путь. Давай разберём, в чём его сила!
Обещание первое: безопасность памяти без сборщика мусора
Представь, что в C программист , как водитель без правил: можно ехать куда угодно и как угодно. Это даёт полную свободу, но и высокий риск аварии. Из‑за таких «аварий» в памяти (например, когда программа пытается использовать уже освобождённую память) возникает около 70 % серьёзных уязвимостей , так выяснили Microsoft и команда Chromium.
Языки со сборщиком мусора вроде JS или Go действуют осторожнее: они сами следят за порядком в памяти. Но за эту безопасность приходится платить , программа работает медленнее, тратит больше ресурсов, а «уборка» памяти может начаться в самый неподходящий момент.
Rust решает эту проблему по‑умному. Вместо того чтобы перекладывать заботу о памяти на программиста (как C) или на рантайм (как JS), он доверяет это компилятору. Система владения , это как умный помощник: он заранее (ещё на этапе компиляции) понимает, когда память можно освободить, и делает это автоматически. При этом в готовой программе нет ни сборщика мусора, ни лишних проверок , только быстрый и безопасный код, как в C.
Обещание второе: нулевые абстракции
Второе обещание звучит как маркетинг, но проверяется дизассемблером: высокоуровневый код компилируется в то же, что ты написал бы руками. Цепочка из filter и map по массиву разворачивается в тот же машинный цикл, что и for со счётчиком, без замыканий, без аллокаций, без виртуальных вызовов. Принцип называется zero-cost abstractions: ты не платишь за то, чем не пользуешься, а тем, чем пользуешься, не смог бы воспользоваться дешевле и вручную.
Для тебя это значит, что весь багаж из ФП-серии раздела 02 здесь применим без оговорок про производительность: итераторы, алгебраические типы, паттерн-матчинг в Rust не сахар поверх медленного рантайма, а конструкции, которые компилятор честно стирает в ноль. В блоке про биты и машину мы откроем дизассемблер и убедимся в этом глазами.
Обещание третье: честный компилятор
Третье обещание самое непривычное. Компилятор Rust отказывается собирать программу, в которой видит возможность ошибки: использование перемещённого значения, висячую ссылку, гонку данных между потоками. Да, гонка данных в Rust это ошибка компиляции, а не баг, который всплывает под нагрузкой раз в неделю.
Из этого следует стиль работы, знакомый тебе по Haskell: подружился с компилятором, значит программа во многом корректна по построению. Рефакторинг перестаёт быть прыжком веры: поменял тип, компилятор перечислил все места, которые перестали сходиться, починил по списку. Цена тоже знакомая: компилятор спорит, и первые недели спорит много. Весь этот раздел построен так, чтобы спор превратился в диалог.
Где это всё живёт
Rust , уже не просто «язык для энтузиастов», а серьёзный инструмент в руках профессионалов. Его используют:
- в ядре Linux (второй по популярности язык после C);
- в драйверах Windows и компонентах Android;
- в браузере Firefox;
- в инфраструктуре Cloudflare и AWS;
- в блокчейнах Solana и Polkadot;
- в популярных инструментах вроде
ripgrep.
В нашем курсе мы тоже не будем ограничиваться простыми примерами. Впереди тебя ждут интересные проекты:
- эмулятор учебного процессора;
- эмулятор NES;
- сетевой игровой движок;
- блокчейн‑узел и мини‑EVM в разделе Web3.
Всё это возможно благодаря главной силе Rust , он сочетает скорость C с надёжностью, которой нет даже у языков со сборщиком мусора.
Демонстрация
Отлично, теперь перейдём к практике , установим всё необходимое. Хорошая новость: как и в Haskell, для Rust есть удобный единый установщик , всё ставится одной командой!
rustup: твой помощник по установке Rust
Представь, что rustup , это универсальный мастер для Rust. Он:
- установит компилятор и все нужные компоненты;
- поможет переключаться между версиями;
- будет вовремя обновлять инструменты.
По сути, rustup для Rust , то же самое, что nvm для Node или GHCup для Haskell.
Давай установим Rust:
- Открой rustup.rs.
- Запусти установщик командой:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
- Соглашайся с настройками по умолчанию , они подойдут для учёбы и работы. Установщик поставит стабильную версию Rust (stable) и добавит нужные пути в настройки твоей оболочки.
- После завершения установки перезапусти терминал.
- Проверь, что всё установилось правильно:
rustc --version
# rustc 1.96.0 (или новее)
cargo --version
# cargo 1.96.0
Не переживай из‑за точного номера версии , главное, чтобы всё работало. Мы будем использовать канал stable , это надёжная версия, которая обновляется каждые шесть недель и гарантирует совместимость.
Обновлять Rust в будущем будет так же просто , достаточно выполнить команду rustup update.
Что у нас появилось после установки:
| Инструмент | Зачем он нужен |
|---|---|
rustc | компилятор , превращает код из .rs файлов в готовую программу |
cargo | главный помощник: создаёт проекты, собирает код, запускает программы, управляет зависимостями |
clippy | умный ассистент , подсказывает, как сделать код лучше и избежать распространённых ошибок |
rustfmt | форматтер , автоматически приводит код к единому стилю, принятому в сообществе Rust |
rust-analyzer | помощник для редактора , показывает подсказки, типы переменных, помогает с автодополнением и рефакторингом |
Не обязательно использовать rustc напрямую , cargo возьмёт на себя большинство задач. А вот clippy и rustfmt стоит начать использовать с самого начала: они помогут писать код в едином стиле и избегать распространённых ошибок.
Настраиваем редактор
Чтобы было удобнее писать код, установи расширение rust‑analyzer для своего редактора:
- Для VS Code , найди расширение rust‑analyzer в магазине расширений.
- Для JetBrains , используй плагин RustRover.
Как проверить, что всё работает:
- Открой любой файл с расширением
.rs. - Наведи курсор на переменную , через секунду появится подсказка с её типом.
Это очень полезная функция: Rust умеет выводить типы автоматически, и не все они явно указаны в коде. Подсказки помогут лучше понимать, что происходит в программе.
Пишем первую программу
Теперь создадим наш первый проект с помощью cargo:
cargo new hello_rust
cd hello_rust
После выполнения команд ты увидишь простую структуру проекта:
hello_rust
├── Cargo.toml
└── src
└── main.rs
Разберём, что есть в проекте:
Cargo.toml, это «паспорт» проекта, похожий наpackage.jsonв JavaScript. Здесь указывается имя, версия и зависимости проекта.src/main.rs, основной файл с кодом.
В Cargo.toml ты увидишь что‑то вроде этого:
[package]
name = "hello_rust"
version = "0.1.0"
edition = "2024"
[dependencies]
Обрати внимание на поле edition , оно указывает версию Rust. Раз в три года выходит новая редакция языка с небольшими улучшениями синтаксиса. При этом компилятор умеет работать с кодом разных версий , так что старый код не сломается.
В файле src/main.rs уже есть простая программа:
fn main() {
println!("Hello, world!");
}
Запустим её:
cargo run
# Compiling hello_rust v0.1.0
# Finished `dev` profile
# Running `target/debug/hello_rust`
# Hello, world!
Что произошло:
cargoсобрал программу в папкеtarget/debug/.- Запустил получившийся исполняемый файл.
- На экране появилось приветствие.
Заметь: получившаяся программа , самодостаточная. Ты можешь скопировать её на другой компьютер (даже если там нет Rust) , она всё равно будет работать!
Один синтаксический звоночек на будущее: println! заканчивается восклицательным знаком, потому что это не функция, а макрос. Пока запомни простое правило: видишь ! после имени, значит эта штука разворачивается в код на этапе компиляции и потому умеет то, что функции не позволено, например принимать разное число аргументов. Серьёзный разговор про макросы ждёт в отдельном блоке.
Цикл разработки: полезные команды
Не всегда нужно сразу собирать готовую программу , часто достаточно просто проверить код. Для этого есть удобная команда:
cargo check
cargo check проверяет код на ошибки с помощью компилятора, но не создаёт исполняемый файл. Благодаря этому команда работает в разы быстрее, чем cargo build.
Хорошая привычка: после написания небольшого фрагмента кода сразу запускай cargo check. Так ты быстро обнаружишь ошибки и исправишь их. В Rust компилятор , твой друг и помощник, а не строгий контролёр!
Вот основные команды, которые ты будешь использовать:
| Команда | Что делает |
|---|---|
cargo check | быстро проверяет код на ошибки, не создавая исполняемый файл |
cargo run | собирает программу и сразу запускает её |
cargo build | собирает debug‑версию программы (для разработки) в папке target/debug |
cargo build --release | собирает оптимизированную версию программы (для релиза) в папке target/release |
cargo clippy | проверяет код на соответствие лучшим практикам и подсказывает, как его улучшить |
cargo fmt | автоматически форматирует код по общепринятым правилам |
Важный момент про --release: debug‑версия программы не оптимизирована и может работать в десятки раз медленнее. Все обещания о высокой скорости относятся именно к оптимизированной версии (--release). Поэтому если хочешь измерить реальную производительность, используй cargo build --release.
Разбираемся с владением: первый опыт
Теперь давай познакомимся с одной из ключевых особенностей Rust , системой владения. Подробный разбор будет в уроке 03, но уже сейчас полезно увидеть, как это работает на практике.
Попробуй добавить в main.rs такой код:
fn main() {
let s = String::from("привет");
let t = s;
println!("{s}");
}
По логике многих языков программирования, после выполнения let t = s переменные t и s должны указывать на одну и ту же строку. И программа должна напечатать «привет».
Но если запустить cargo check, мы увидим ошибку:
error[E0382]: borrow of moved value: `s`
--> src/main.rs:4:15
|
2 | let s = String::from("привет");
| - move occurs because `s` has type `String`,
| which does not implement the `Copy` trait
3 | let t = s;
| - value moved here
4 | println!("{s}");
| ^^^ value borrowed here after move
|
help: consider cloning the value if the performance cost is acceptable
|
3 | let t = s.clone();
| ++++++++
Это работает borrow checker , специальный механизм компилятора Rust. Давай разберём сообщение об ошибке по шагам , так ты научишься понимать, что именно пошло не так:
- Заголовок ошибки (
error[E0382]) говорит о проблеме: ты пытаешься использовать значение после того, как оно было перемещено. - Стрелочки и пояснения показывают всю историю:
- в строке 2 создаётся значение, его владелец ,
s; - в строке 3 владение передаётся переменной
t(value moved here); - в строке 4 программа пытается использовать
s, ноsбольше ничего не владеет.
- в строке 2 создаётся значение, его владелец ,
- Совет от компилятора (
help) предлагает готовое решение: если тебе действительно нужны две копии строки, используй.clone().
Что здесь произошло? В Rust у каждого значения может быть только один владелец. Когда ты пишешь let t = s, ты не копируешь строку и не создаёшь вторую ссылку на неё , ты передаёшь владение. Теперь строкой владеет t, а переменная s становится «недействительной». Компилятор знает это и не позволит тебе использовать s.
Зачем такая строгость? Это как раз и обеспечивает безопасность памяти без сборщика мусора:
- понятно, кто отвечает за освобождение памяти;
- исключено двойное освобождение памяти;
- существенно снижена вероятность утечек памяти по сравнению с языками без системы владения. Тем не менее, утечки всё ещё возможны, например, при использовании
mem::forgetили создании циклических ссылок черезRc.
Всё это проверяется ещё на этапе компиляции , до запуска программы.
Мы подробно разберём владение, перемещение (move) и освобождение памяти (drop) в уроке 03. А пока запомни простую аналогию: каждое значение в Rust , это предмет, который может находиться только у одного хозяина. Передал его кому‑то , сам больше им не владеешь.
И заметь, какого качества сама ошибка: история значения по шагам, точные места в коде, рабочее исправление. Сообщения rustc считаются эталоном в индустрии, и у любого кода ошибки есть развёрнутое объяснение:
rustc --explain E0382
Как учить Rust с удовольствием
У Rust иногда говорят, что он сложный. Но на самом деле большая часть трудностей возникает из‑за привычек, которые мы приносим из других языков. Давай договоримся сразу , так будет проще и приятнее:
Читай ошибки целиком, не спеши гуглить. В других языках ошибка часто означает «всё сломалось», и хочется сразу найти решение в поиске. В Rust всё иначе: сообщение об ошибке , это мини‑урок. Оно покажет, где проблема, объяснит, почему она возникла, и даже предложит, как исправить. Если читать ошибки внимательно, учить язык будет в разы легче.
Не торопись использовать unsafe. В Rust есть ключевое слово unsafe, которое позволяет обойти некоторые гарантии безопасности (например, при работе с сырыми указателями). При этом базовые проверки компилятора, включая проверку заимствований (borrow checking), остаются активными. Но оно нужно только для особых задач (например, при работе с железом). На первых порах оно тебе не понадобится. Если кажется, что без unsafe не обойтись , скорее всего, можно решить задачу иначе. Мы разберём unsafe позже, когда придёт время.
Используй unwrap с осторожностью. В Rust функции, которые могут завершиться с ошибкой, возвращают Result. Метод .unwrap() говорит: «Достань значение, а если ошибка , заверши программу». В учебных примерах он допустим, но не стоит злоупотреблять им. Лучше учиться обрабатывать ошибки правильно , этому посвящён отдельный урок.
Доверяй компилятору. Бывает, что код выглядит правильным, но компилятор его не принимает. Вместо того чтобы искать обходные пути, попробуй подумать: а правильно ли спроектированы данные? Чаще всего ошибка компилятора , это подсказка, что в логике есть неясность, которую другой язык мог бы просто проигнорировать.
И ещё один хороший совет: запускай cargo clippy после каждой решённой задачи. Этот инструмент знает сотни лучших практик и поможет писать код так, как это делают опытные разработчики. По сути, это бесплатный помощник‑ревьюер, который доступен тебе с первого дня.
Практика
Слов про язык было много, пора написать на нём первую функцию. Задача нарочно крошечная: один format! и тело-выражение, зато тесты настоящие.
ДЗ
Дальше
В уроке 02 мы начнём говорить на Rust по‑серьёзному: разберём скаляры и составные типы, изменяемость и shadowing, if как выражение и четыре вида циклов. После этого урока ты сможешь уверенно читать и писать простой Rust‑код, не спотыкаясь о синтаксис.