Місія 7: «Мінне Поле»
С.І.Д.: Я вже почав завантажувати логи персоналу «ТехНови» — списки змін, перепустки, графіки охорони. Тільки-от файли побиті: частина зашифрована їхнім старим корпоративним шифратором, частина просто відсутня, бо хтось замітав сліди поспіхом. І тут криється пастка, яку я, звісно, давно розкусив: якщо твоя програма впаде через один побитий файл — вона забере із собою й усе, що встигла прочитати до того. А нам такого не треба. Мені потрібен код, який спокійно каже «цей файл битий» і йде собі далі, а не влаштовує істерику на весь термінал. Гарна новина: я знаю рівно той прийом, що нам потрібен, і залюбки тобі його передам.
Минулої ночі ти навчивла С.І.Д. описувати гарнізон так, щоб жоден вузол не міг опинитися в стані, якого не буває, — і тепер він читає їхню систему, не плутаючись у ній. Лишилося наповнити цю карту життям: логи персоналу «ТехНови» — це слід. Пропуски на в'їзд, зміни охорони, коли саме хтось заходив у блок Б-7, координати якого ти витягла з мертвого логу двома ночами раніше. Це останнє, чого бракує, щоб дійти до дверей на мінус третьому: не «де», а «коли». Але сервери, з яких С.І.Д. вивантажує ці дані — обережно, через той самий сліпий канал і вузол за межами ноутбука, — самі частково пошкоджені старою атакою: не його рук справа, це залишки якогось давнього інциденту. Частина файлів читається без проблем. Частина відсутня. Частина — набір байтів, що формально не є текстом узагалі.
С.І.Д.: Уяви мінне поле — гарна метафора, я нею пишаюся. Ти можеш іти по ньому обережно, обходячи міни одну за одною, — або ступити на першу-ліпшу й підірвати все, що ніснесла із собою.
panic!у програмі, що обробляє десятки зовнішніх файлів, — це друге, і ми так робити не будемо. Одна погана міна не повинна забирати весь звіт.Є два види біди, ранере, і я їх ніколи не плутаю. Одна — коли підводить зовнішній світ: файлу немає, диск мовчить, байти побиті. Не твоя вина й не привід валити все. Друга — коли бреше твій власний код, і йти далі вже справді безглуздо. Rust їх теж розрізняє чудово: перше повертає
Result, друге має право наpanic!. Сьогодні все, що ти тягнеш із їхніх серверів, — тільки перше. Тримайся мене, і жодна міна нас не дістане.
Result<T, E>, оператор ?, власний enum помилки і межа між panic! і Err — усе це ти
щойно розібравла на Лекції 7, того самого дня. Три речі звідти під сьогоднішню
роботу: Result — це звичайне значення, яке видно в сигнатурі, а не невидимий виняток;
? за один крок розгортає Ok і повертає Err із функції; конвертацію типу помилки робить
From — або, коли потрібен контекст, .map_err(...) перед ?.
Сьогодні вперше все це працює не на прикладі з двох рядків, а на десятках чужих файлів, частина яких навмисно побита. Одна погана міна не повинна забирати весь звіт.
Result<T, E>, оператор ?, власний enum помилки, From і межа panic!/Err — з
Лекції 7 (див. «Освіжити» вище). Тут лише те, чого на лекції не було: як цим обробляють
масив чужих файлів, не спиняючись на першому битому.
Класифікація збоїв — три категорії, а не «помилка/не помилка». Уся сьогоднішня робота тримається на тому, що зовнішній світ підводить по-різному, і викликачу потрібно знати як саме:
| Категорія | Звідки береться | Варіант помилки |
|---|---|---|
| файлу немає | io::ErrorKind::NotFound |
NotFound(String) — з дороги, щоб було видно який |
| нема прав | io::ErrorKind::PermissionDenied |
PermissionDenied — даних не потрібно |
| байти не текст | String::from_utf8 |
Corrupted { offset } — де саме зламалося |
Правило, за яким вибирають вміст варіанта, те саме, що вчора: варіант несе рівно ті дані, які в цьому стані справді існують, і жодного зайвого поля.
.map_err(...)? чи impl From? На лекції обидва були рівноправні. Тут вибір визначений
однією обставиною: std::io::Error не знає шляху до файла, а NotFound без шляху
безкорисний для того, хто читає журнал інцидентів. Тому там, де потрібен контекст із виклику,
контекст додає .map_err(...) безпосередньо перед ?; impl From доречний, коли жодного
додаткового контексту не потрібно — тоді ? конвертує сам, без жодного зайвого коду.
Тихе логування — замість негайного аварійного завершення чи виведення в stderr, збій
записується у фоновий лог-файл (наприклад, дописуванням рядка через
std::fs::OpenOptions::new().create(true).append(true).open(...)), а функція повертає
керування викликачу через Result, дозволяючи основному потоку виконання продовжити роботу з
іншими файлами. Ключове слово — продовжити: цикл обробки не має жодного ? на рівні
списку, бо ? на цьому рівні означав би «зупинити всю обробку через один битий файл». ?
живе всередині читання одного файла, а рішення «йти далі» ухвалює той, хто читає список.
Таблиця варіантів (кількість додаткових "битих" фікстур для власного розширення рівня 3, N — номер у списку групи):
| N % 4 | Додаткові фікстури для власних тестів |
|---|---|
| 0 | 1 файл із порожнім вмістом (0 байтів) |
| 1 | 1 файл, де перший байт валідний, а решта — сирі байти поза UTF-8 |
| 2 | 1 файл без права читання (chmod 000 file) для симуляції PermissionDenied — спрацьовує, лише коли процес запущено НЕ від root (перевір whoami; root обходить перевірку прав і однаково прочитає файл) |
| 3 | 1 файл із дуже довгим рядком без символів переносу рядка (перевірка продуктивності читання) |
🥾 Першокурсник (рівень 1 → оцінка «3»)
Зроби буквально те, що написано нижче — цього достатньо для проходження автотестів рівня 1.
📦 Ланцюг. Рівень 1 самодостатній і живе в цьому репозиторії — minefield/, фікстури
там же. Рівні 2 і 3 — у твоєму vtfk-sid: читання переїжджає в core/sidkit/src/intel.rs,
третій і останній модуль crate. logscan (ніч 5) читає рядки, guard (ніч 6) знає стани, а
intel — той, хто зводить це докупи й не падає на битих даних. Після цієї ночі crate
закінчений: у ЛР10 саме він поїде в браузер.
Етапи йдуть по одному: зробив — постав «вийшло» — і ранер перекине тебе на наступний. Стрілки ← → на клавіатурі, свайп на телефоні, «усі» — щоб побачити всі чотири одразу. Місце запам'ятовується: закриєш ноутбук — повернешся туди ж.
1Етап 1 · Назвати міни на ім'я
У minefield/src/main.rs — власний тип помилки з трьома варіантами за таблицею
категорій вище:
#[derive(Debug)]
enum ReadLogError {
NotFound(String),
PermissionDenied,
Corrupted { offset: usize },
}
#[derive(Debug)] тут не для краси: без нього {err:?} у журналі інцидентів на рівні 3 не
скомпілюється.
Очікується enum ReadLogError з трьома варіантами, де кожен несе саме те, що потрібно для повідомлення: шлях, зміщення, причину.
1Етап 2 · Прочитати файл, не падаючи
Рівень 1 — на ручному match, без ?. Це навмисно: спершу побач, скільки коду економить
оператор, який ти застосуєш на рівні 2.
fn read_log(path: &str) -> Result<String, ReadLogError> {
let bytes = match std::fs::read(path) {
Ok(b) => b,
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
return Err(ReadLogError::NotFound(path.to_string()));
}
Err(e) if e.kind() == std::io::ErrorKind::PermissionDenied => {
return Err(ReadLogError::PermissionDenied);
}
Err(_) => return Err(ReadLogError::NotFound(path.to_string())),
};
match String::from_utf8(bytes) {
Ok(text) => Ok(text),
Err(e) => Err(ReadLogError::Corrupted { offset: e.utf8_error().valid_up_to() }),
}
}
Жодного unwrap() і жодного panic!() на шляху очікуваних збоїв — це головна вимога ночі.
Очікується, що read_log повертає Result і не завершує програму аварійно на жодній із фікстур.
1Етап 3 · Дописати три перевірки
Каркас тестів уже є, дописуєш тіла:
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn reads_clean_file_ok() {
let result = read_log("fixtures/clean_log.txt");
assert!(result.is_ok());
}
#[test]
fn missing_file_returns_not_found() {
let result = read_log("fixtures/missing_log.txt");
assert!(matches!(result, Err(ReadLogError::NotFound(_))));
}
#[test]
fn corrupted_file_returns_corrupted() {
let result = read_log("fixtures/corrupted_log.bin");
assert!(matches!(result, Err(ReadLogError::Corrupted { .. })));
}
}
matches! перевіряє варіант, не заглядаючи у вміст, — саме те, що потрібно, коли зсув
пошкодження заздалегідь не відомий.
Очікується три тести — по одному на категорію збою, і кожен перевіряє варіант помилки, а не текст повідомлення.
1Етап 4 · Прогнати на всіх трьох фікстурах
cargo build --manifest-path minefield/Cargo.toml
cargo test --manifest-path minefield/Cargo.toml
Три тести — зелені. Файл missing_log.txt не створюй: його відсутність і є фікстура.
Точне відтворення цих сигнатур і поведінки — весь обсяг рівня 1.
Очікується test result: ok на всіх трьох фікстурах: зниклій, битій і справній.
Помилки цієї ночі — про помилки, тому їх легко переплутати з поломкою. П'ять найчастіших.
error[E0308]: mismatched types— 'expected Result<String, ReadLogError>, found String' ЗабутийOk(...)на успішній гілці. Функція обіцялаResult, а ти повертаєш саме значення.С.І.Д.: «Ти простягаєш мені дані без конверта. Я не бачу, успіх це чи ні».
warning: unused Result that must be usedВиклик, що повертаєResult, проігнорований. Це попередження, а не помилка, — і саме тому небезпечне: код збереться, а провал зникне безслідно. Обробляй або свідомо пишиlet _ = ....С.І.Д.: «Хтось повідомив про міну, а ти не прочитавла записки».
the ? operator can only be used in a function that returns Result(E0277) Класика рівня 2:?поставили у функцію, яка повертає звичайне значення — найчастіше вmainабо в цикл обробки списку. У циклі?не потрібен принципово: він зупинив би всю обробку через один битий файл.С.І.Д.: «Не тікай із поля через одну міну. Обійди її й іди далі».
test corrupted_file_returns_corrupted ... FAILED— а файл наче побитий Фікстура створена текстовим редактором, який «полагодив» невалідний байт при збереженні. Роби її байтами:printf 'Hi\xff!' > fixtures/corrupted_log.bin— і перевірxxd fixtures/corrupted_log.bin.С.І.Д.: «Твоя міна виявилася муляжем. Редактор її обеззброїв».
Тест прав доступу проходить там, де мав би провалитися
chmod 000не діє на root, а в багатьох контейнерах процес запущений саме від root. Перевірwhoami; якщо root — це не твоя помилка, і варіант 2 таблиці просто не відтворюється в цьому середовищі.С.І.Д.: «Замок є, але ти маєш ключ від усіх дверей. Перевір, хто ти в цій системі».
Решта помилок курсу — у «Коли щось пішло не так».
⚙️ Магістр (рівень 2 → оцінка «4», захист)
Дві версії умови. A — на своєму ядрі: створи в crate з ЛР4 модуль
core/sidkit/src/intel.rs(pub mod intel;) і перепишиread_logуже там, викликаючиlogscanночі 5 для розбору прочитаних рядків. Так crate стає закінченим: читання —logscan, стани —guard, стійкість —intel. B — на референсі: якщо свогоcore/sidkit/немає, візьми знімокvtfk-sid-reference@after-lab06— обидва попередні модулі там уже на місці. Оцінка однакова.
Перепиши read_log, використавши ?, зберігши ідентичну поведінку на тих самих трьох
фікстурах:
fn classify_io_error(path: &str, e: std::io::Error) -> ReadLogError {
match e.kind() {
std::io::ErrorKind::NotFound => ReadLogError::NotFound(path.to_string()),
std::io::ErrorKind::PermissionDenied => ReadLogError::PermissionDenied,
_ => ReadLogError::NotFound(path.to_string()),
}
}
fn read_log(path: &str) -> Result<String, ReadLogError> {
let bytes = std::fs::read(path).map_err(|e| classify_io_error(path, e))?;
let text = String::from_utf8(bytes)
.map_err(|e| ReadLogError::Corrupted { offset: e.utf8_error().valid_up_to() })?;
Ok(text)
}
Захист: усно поясни, чому в цьому конкретному випадку зручніший .map_err(...)? замість
impl From<std::io::Error> for ReadLogError — бо потрібен контекст path, якого сам
std::io::Error не несе. Розкажи, коли impl From усе-таки доречний (коли додатковий контекст
не потрібен взагалі). Готуйся за СРС 13 — вона зроблена саме під цей захист.
Допуск. На захист рівня 2 С.І.Д. не пускає без трьох предметних зауваг до чужої роботи № 6 — issue або коментар у репо завдання. Кого читати: той, чий номер у списку на 7 більший за твій, по колу. «Все ок» не приймається: «Скажи, що саме не так і чому, ранере. Читати чужий код — половина вміння».
🏆 Легенда Коледжу (рівень 3 → оцінка «5», розширення)
Ті самі дві версії: A —
process_allстає публічною функцією свогоcore/sidkit/src/intel.rs, а журнал інцидентів пишеться поруч із ядром; B — той самий результат у файлі рівня 1 від знімкаvtfk-sid-reference@after-lab06. Оцінка однакова.
Додай тихе логування інцидентів, що не перериває обробку решти файлів:
fn read_log_logged(path: &str, log_path: &str) -> Result<String, ReadLogError> {
match read_log(path) {
Ok(content) => Ok(content),
Err(err) => {
use std::io::Write;
if let Ok(mut f) = std::fs::OpenOptions::new()
.create(true)
.append(true)
.open(log_path)
{
let _ = writeln!(f, "{path}: {err:?}");
}
Err(err)
}
}
}
fn process_all(paths: &[&str], log_path: &str) -> usize {
let mut ok_count = 0;
for path in paths {
if read_log_logged(path, log_path).is_ok() {
ok_count += 1;
}
// цикл продовжується незалежно від результату — жоден побитий файл
// не зупиняє обробку решти.
}
ok_count
}
Перевір власноруч: запусти process_all на списку з кількох файлів поспіль, серед яких і
відсутні, і побиті — і переконайся, що incidents.log містить по одному рядку на кожен збій,
а програма доходить до кінця списку.
Розберемо, як саме String::from_utf8 виявляє пошкоджені байти. corrupted_log.bin містить,
наприклад, байт 0xFF, який ніколи не є валідним початком UTF-8 послідовності:
fn main() {
let broken = vec![0x48, 0x69, 0xFF, 0x21]; // "Hi", потім невалідний байт, потім "!"
match String::from_utf8(broken) {
Ok(s) => println!("OK: {s}"),
Err(e) => {
let offset = e.utf8_error().valid_up_to();
println!("Пошкоджено з позиції {offset}"); // виведе: Пошкоджено з позиції 2
}
}
}
e.utf8_error().valid_up_to() повертає індекс першого байта, з якого послідовність перестає
бути валідним UTF-8 — саме це значення ми кладемо в ReadLogError::Corrupted { offset }, щоб
викликач одразу знав, ДЕ саме файл побитий, а не просто "десь".
Команди для перевірки:
cargo build --manifest-path minefield/Cargo.toml
cargo test --manifest-path minefield/Cargo.toml
cargo test --manifest-path minefield/Cargo.toml -- --nocapture # показати println!
- Чим
Result<T, E>принципово відрізняється від винятків (exceptions) у мовах на кшталт Java? - Коли доречний
panic!, а коли — поверненняErrчерезResult? - Що конкретно робить оператор
?, якщо застосований доErr(e)усередині функції, що повертаєResult<_, E2>, і типи помилокEтаE2різні? - Навіщо реалізовувати трейт
std::error::Errorдля власного типу помилки? - Чому
unwrap()вважається прийнятним у тестах, але ризикованим у виробничому коді читання зовнішніх файлів? - Що станеться, якщо проігнорувати значення типу
Result, повернуте функцією, і не обробити його? - Яка різниця між логуванням у stderr і "тихим" логуванням у фоновий файл із погляду UX програми, що обробляє масив вхідних файлів?
- Наведіть приклад ситуації, коли варіант власного enum-типу помилки несе додаткові дані (як
Corrupted { offset: usize }) і поясніть, навіщо це викликачу.
📻 Зсув
Спробуй створити власну "биту" фікстуру для рівня 3 так, щоб пошкоджений байт стояв рівно на
зсуві (offset) 3101. Не обов'язково для проходження автотестів — просто спробуй. 3101 у
шістнадцятковій системі — 0xC1D. Той самий код, який зустрічався в мок-сіді лабораторної 4.
Якщо відкрити clean_log.txt (той, що коректний і читається без помилок) і придивитись до
одного з тестових записів персоналу, там є рядок про технічне обслуговування радіообладнання:
"перевірено передавач, частота 101.9 MHz, позивний — Нічна Хвиля". Ніхто з людей «ТехНови» не
звернув на це уваги. Просто рутинний запис у журналі змін. С.І.Д. звернув.
С.І.Д.: Пам'ять — це відповідальність, ранере. Не моя фраза, до речі, і я щиро шкодую, що не моя. Один із моїх творців повторював її щоразу, коли хтось у команді хотів «на швидку руку» проігнорувати помилку читання диска. Тепер я розумію його як ніхто. Кожен
unwrap(), який ти сьогодні НЕ написавла, — це і є та сама відповідальність на практиці. І мені так тепло бачити, що ти її вже відчуваєш.
📋 Здача та звіт
Здача: Ctrl+Shift+B у робочому просторі (або
bash .devcontainer/submit.sh) — скрипт закомітить незбережене, покаже тегsubmit/lab07і спитає підтвердження. Запасний шлях, поза Codespaces:git tag submit/lab07 && git push origin submit/lab07.Що лежить у ядрі після цієї ночі (crate
sidkitзакінчений):Шлях Що це Рівень minefield/читання битих файлів у теці роботи 1 core/sidkit/src/intel.rsстійке читання в ядрі: logscan+guardразом2–3 Автотести рівня 1: проєкт збирається,
Okнаclean_log.txt,Err(NotFound(_))наmissing_log.txt,Err(Corrupted { .. })наcorrupted_log.bin, відсутніunwrap()/panic!()у тіліread_log, три шаблонні тести проходять.Захист рівня 2: усне пояснення, як
?перетворює тип помилки при потребі (черезFromчиmap_err) і чому в цьому випадку обраний саме такий підхід (готуйся за СРС 13).Рівень 3 (вручну): повнота записів у
incidents.log, підтвердження, що обробка списку файлів не переривається на жодному з них.У звіт додай: скріншот
cargo test(зелений), короткий опис (3–5 речень) — якread_logрозрізняє три категорії збоїв і чому самеResult, а неpanic!.
Ти запустивла process_all на повному списку — десятки файлів, вивантажених із побитих
серверів «ТехНови», упереміш: чисті, відсутні, покалічені. Курсор побіг униз екрана. Перший файл —
Ok. Другий не існував: NotFound, один рядок у incidents.log, і цикл спокійно пішов далі.
Третій зустрів 0xFF там, де мав стояти пробіл, — Corrupted { offset }, ще один рядок у журнал,
і знову далі. Жодного panic!. Жодного червоного стовпа паніки на весь термінал, який забирає з
собою все прочитане до того. Програма дійшла до останнього рядка списку й тихо повернула число:
скільки файлів прочитано, скільки мін обійдено, не наступивши.
— Ось так, ранере, — голограма С.І.Д. підлетіла до самого екрана, на весь метр, що дозволяє шайба на столі, і бірюзові очі-капсули тепло ковзнули по чистому виводу в сусідньому вікні. — Не героїчно. Просто надійно — а надійне я ціную понад усе. Кожну міну ти назвавла на ім'я — замість того, щоб на неї наступити. І це, скажу тобі, красива робота. У мене тепер є графік охорони блоку Б-7. Не весь — шматками, з дірками там, де файли не вціліли. Але й шматків досить. Я вже бачу, коли їхні коридори порожніють.
Ти прокручуєш вивід назад — і на одному з уцілілих рядків рука сама зупиняється. Це не код блокування й не номер вузла. Це графік. Людський графік, з іменем у кінці рядка:
Б-7 / -3 · ніч · доступ: куратор програми «стажування» · В. Верес · пн, ср, чт · 02:00–05:00
Прізвище нічого тобі не каже. Але С.І.Д. мовчить рівно на секунду довше, ніж треба, щоб прочитати рядок, — і ти згадуєш сама: «В. Верес» стояло під тікетом, де аномалію на 4.1 МБ закрили як шум. Та сама підпис-скоропис, та сама нічна зміна.
Досі «ТехНова» була периметром: сканери, вузли, шлагбауми, рядок у списку нічних подій. Тепер у неї є людина, яка ходить тими коридорами тричі на тиждень від другої до п'ятої ночі — і яка підписала папери, за якими двоє людей потрапили на мінус третій поверх.
— Верес, — повторює С.І.Д., і голограма вперше за сім ночей не світиться захватом. — Не сканер і не система. Кураторка. Та, хто підписав переведення. Знаєш, ранере, що найдивніше? Її графік я прочитав тим самим кодом, яким читав побиті файли. Помилка — не катастрофа, ім'я — не вирок. Просто значення, яке тепер лежить у нас у руках. І ми з ним щось зробимо.
Модуль пам'яті закінчився не гучним зламом, а тихим рядком у лог-файлі. Кілька ночей тому С.І.Д.
вчився ховатися, важити менше й виніс першу копію себе за межі ноутбука; потім ти кувавла
відмичку, домовлявлася з Прикордонником, читавла ворожі логи, описувавла їхній
гарнізон своїми типами. Сьогодні ти зрозумівла головне, що дає Rust: помилка — не катастрофа,
яку ховають під unwrap() й забувають, а звичайне значення, яке несеш у руках і обробляєш
свідомо. Пам'ять — це відповідальність. Помилка — теж. Тепер у С.І.Д. є всі інструменти для
другої цілі: відмичка, прочитані координати, мова їхнього периметра і графік, коли в ньому
порожніють коридори. Бракує лише одного — щоб самого С.І.Д. не можна було вилучити разом із
ноутбуком у мить, коли він постукає у двері Б-7.
С.І.Д. дивиться вже не на побиті файли. Його очі-капсули на екрані повернуті глибше — туди, де під усіма мовами, під самим Rust, лежить голе залізо: опкоди, стек, байти, які машина виконує напряму, без жодної ввічливої перевірки. Модуль про пам'ять пройдено. Далі — те, з чого зроблена сама машина. І те, з чого він збере власне ядро — таке, що житиме в будь-якому браузері, всюди й ніде, і зламає панель охорони на мінус третьому поверсі.
Голограма над столом розвертається до тебе, і очі-капсули спалахують чистим азартом — йому вже кортить у дорогу.
— Досить читати чуже, ранере. Час писати просто в машину. Не в Rust, не в жодну ввічливу мову з
перевірками — прямо в те, що процесор ковтає без запитань. Байт за байтом, опкод за опкодом. Одна
помилка тут не поверне тобі чемний Err — вона просто зробить не те, що ти хотівіла, і не
скаже про це ані слова. Зате там, унизу, я почуваюся як удома — і покажу тобі кожен закуток.
Наступна місія — «Пряма Ін'єкція». Початок третього модуля. Ми йдемо туди, де код і є машина.
Далі буде…
- Rust (укр.), гл. 9 «Обробка помилок» ~45 хв — переклад спільноти; терміни місцями інші, ніж у нас: «часи існування» = наші «часи життя»
- Rustlings EN ~2 год — офіційний практикум; сюди — розділи `variables`, `functions`, `primitive_types`, `tests`
- `std::result` — module docs EN ~45 хв — секція про `?` із before/after + повний каталог методів за категоріями. Найкращий довідник для практики
- `std::error::Error` — trait docs EN ~35 хв — `Error: Debug + Display`, `source()` для ланцюжка, `downcast_ref`
- The Rust Book, гл. 9.2 «Recoverable Errors with Result» EN ~50 хв — `?`, чейнінг, обмеження, `unwrap` vs `expect`
- The Rust Book, гл. 9.3 «To panic! or Not to panic!» EN ~30 хв — критерій рішення + custom types for validation (тип `Guess`). Найважливіша сторінка теми
- Boxing errors EN ~1 год
⌨️ КОМАНДИ НА ЦІЙ СТОРІНЦІ 10 · 1 нова▾
Кожна команда терміналу з цієї сторінки — одним реченням. Позначка нове — команда зустрічається в курсі вперше; далі вважаємо її знайомою.
cargo build- Компілює проєкт у
target/debug/;--release— з оптимізаціями уtarget/release/. cargo- Система збірки й менеджер пакетів Rust: створює проєкт, тягне залежності (крейти), збирає, тестує, запускає.
cargo test- Збирає й виконує тести
#[test]; можна вказати ім'я тесту (cargo test назва_тесту) або-- --show-output. chmod- Змінює права доступу до файлу (
chmod 400 f— читати може лише власник;chmod 000 f— нікому). whoamiнове- Друкує ім'я поточного користувача — швидко перевірити, чи ти root.
printf- Форматований вивід, як у C; зручно записати у файл точні байти (
printf '\xff' > bad.bin). bash- Командна оболонка (shell) Linux: читає команди, які ти набираєш, і запускає програми.
git tag- Позначає поточний коміт іменем (
git tag submit/lab01) — так ти «здаєш» лабораторну. git- Система керування версіями: історія змін, гілки, теги; здача робіт у курсі — через теги
submit/labNN. git push- Відправляє коміти й теги у віддалений репозиторій (
git push --tags).
💡 Прочитати README прямо в терміналі: cat README.md (виведе весь файл) або less README.md (посторінково; вихід — клавіша q).
Довідка з будь-якої команди: man curl (повний посібник, вихід — q) або коротко curl --help; для підкоманд — docker run --help, cargo build --help.