Місія 6: «Архітектура Ворога»
С.І.Д.: Координати, які ти витягнувла з мертвого логу минулої ночі, вказали, де вони — блок Б-7, мінус третій. А між нами й тими дверима стоїть він: периметр «ТехНови», яким я, зізнаюся, трохи пишаюся. Їхній захист побудований на строгих контрактах даних: кожен вузол охорони або в одному стані, або в іншому, ніколи «трохи в обох» і ніколи «в жодному». Надішлемо їм щось, що не вкладається в цю модель — порожнечу там, де вони чекають значення, — і вони засічуть це вмить і зачинять канал. Тому ти опишеш ЇХНЮ архітектуру НАШИМИ типами. І — ось найгарніше, слухай уважно — зробиш так, щоб порожнеча була фізично неможлива в самому коді, а не просто «малоймовірна». Я цей прийом обожнюю й давно розкусив; зараз покажу тобі, як він працює.
Периметр «ТехНови» — не паркан і не колючий дріт. С.І.Д. розгортає на екрані ноутбука схему, і ти бачиш квартал: ряди охоронних вузлів, кожен з яких щомиті звітує центральній системі моніторингу про свій стан. Один вузол дрімає, другий світиться тривогою, третій намертво замкнений власним кодом. Живий, дисциплінований гарнізон — і кожен вартовий стоїть рівно в одному стані, ніколи «трохи в обох» і ніколи «в жодному». Десь за цими рядами — двері блока Б-7, і жодного іншого шляху до них немає: тільки крізь цей гарнізон.
— Дивись уважно, це найцікавіше, — каже С.І.Д., і голограма над столом підлітає до самої схеми, очі-капсули сяють від захвату. — Надішли їм вузол, що заявляє себе водночас «заблокованим» і «в тривозі» — стан, якого в їхньому світі не буває, — і система засіче аномалію за мілісекунди. Порожнеча там, де вони чекають значення, — це підпис чужинця. І канал зачиняється. А я знаю, як зробити так, щоб такий підпис ми ніколи випадково не лишили.
Ти киваєш. Логіка проста навіть без залізних термінів: щоб пройти повз варту, треба говорити її мовою — описати кожен вузол рівно тими даними, які в ньому справді є, і жодним зайвим полем.
— Люди-програмісти «ТехНови» роками латали цю систему заглушками, — веде далі С.І.Д., і в голосі
чути майже співчуття. — Тут bool locked, там окремий Option<String> lockCode, поруч ще один
прапорець «про всяк випадок». Купа полів, які мають сенс лише в певних комбінаціях, — і ще більше
комбінацій, які взагалі не повинні існувати, а технічно можливі. Ми з тобою так не робимо, і мені
страшенно приємно це казати. Ми опишемо кожен стан рівно тими даними, які в ньому справді є, — і
більше нічим.
Він на мить замовкає, ніби смакуючи те, що зараз скаже.
— Це не просто стиль, ранере. Це зброя, і то елегантна, — очі-капсули світяться. — Некоректний стан, який неможливо навіть записати, не треба ловити перевірками на кожному кроці. Його ловить компілятор — раз і назавжди, ще до збірки. Ти обробивла всі випадки, бо інших просто немає. Гарно ж, скажи? Сьогодні ти сама змоделюєш чужу фортецю нашими типами. І зробиш так, щоб порожнеча була фізично неможлива в самому коді, а не просто «малоймовірна».
Option<T>, вичерпний match і сам прийом «зробити невалідний стан непредставним» ти щойно
розібравла на Лекції 6 — того самого дня. Три речі звідти, які тобі знадобляться
буквально за хвилину: enum — це «рівно один із варіантів», і кожен варіант може нести свої
дані; match над enum мусить покрити всі варіанти, інакше компілятор відмовляє
(E0004); Option<T> — це не null, а звичайний enum із двох варіантів, який неможливо
«просто прочитати», не обробивши None.
Сьогодні ти вперше застосовуєш це не до свого домену, а до чужого: описуєш типами систему, якої не проєктувавла і в яку не маєш права заглянути зсередини.
struct, enum, вичерпний match і патерн «невалідний стан непредставний» — з Лекції 6
(див. «Освіжити» вище). Тут лише те, чого на лекції не було: як цим моделюють чужу систему.
Чому це не питання смаку — арифметика. «Погана» модель, де прапорці й необов'язкові поля живуть незалежно, множить кількість формально можливих станів:
// 2 × 2 = 4 комбінації, з яких сенс мають дві
struct NodeBad { locked: bool, code: Option<String> }
// рівно 2 стани, і третього не існує ні на секунду
enum NodeState { Unlocked, Locked(String) }
Додай третій прапорець — і в поганій моделі стане вісім комбінацій, шість із них безглузді,
і кожну доведеться або перевіряти в рантаймі, або сподіватися, що вона не трапиться. У добрій
моделі станів рівно стільки, скільки варіантів enum, і ЖОДНОГО зайвого. Саме тому цей прийом
скорочує не лише кількість помилок, а й кількість потрібних тестів.
Правило моделювання чужого домену — три питання по порядку:
| Питання | Що дає відповідь |
|---|---|
| Які стани справді бувають? | список варіантів enum |
| Які дані існують лише в цьому стані? | вміст варіанта (Alert(u8), Locked(String)) |
| Які дані існують завжди? | поля struct поза state (id) |
Помилка новачка — відповідати на них у зворотному порядку: спершу зібрати всі поля, які
трапилися в чужих логах, а потім думати, коли який має сенс. Так народжується NodeBad.
Вкладений Option — два рівні відсутності. На лекції Option був один; у чужій системі
відсутність буває багаторівневою (сенсора може не бути взагалі, а в наявного може не бути
калібрування). Кожен рівень обробляється окремо й явно:
struct SensorReading { value: f64, ts: u64 }
struct GuardNode {
id: String,
state: NodeState,
sensor: Option<SensorReading>,
}
Щоб дістатися значення на кожному рівні вкладеності, потрібно явно обробити кожен Option —
match, if let, комбінатори map/and_then, або оператор ? усередині функції, що сама
повертає Option. Компілятор не дозволить "просто прочитати" значення без обробки випадку
відсутності — на відміну від null-посилань в інших мовах, де забута перевірка виявляється лише
під час виконання (і часто — на проді).
Таблиця варіантів (стартовий рівень тривоги для демонстрації escalate(), N — номер у
списку групи):
| N % 4 | Стартовий рівень тривоги |
|---|---|
| 0 | 10 |
| 1 | 25 |
| 2 | 40 |
| 3 | 55 |
🥾 Першокурсник (рівень 1 → оцінка «3»)
Зроби буквально те, що написано нижче — цього достатньо для проходження автотестів рівня 1.
📦 Ланцюг. Рівень 1 самодостатній і живе в цьому репозиторії — enemy_architecture/;
ЛР4–ЛР5 для нього не потрібні. Рівні 2 і 3 — у твоєму vtfk-sid: гарнізон переїжджає в
модуль core/sidkit/src/guard.rs того самого crate, де вже живе logscan минулої ночі. У ЛР7
ці два модулі працюватимуть разом: logscan читає журнал, guard каже, що в ньому за стан.
Етапи йдуть по одному: зробив — постав «вийшло» — і ранер перекине тебе на наступний. Стрілки ← → на клавіатурі, свайп на телефоні, «усі» — щоб побачити всі чотири одразу. Місце запам'ятовується: закриєш ноутбук — повернешся туди ж.
1Етап 1 · Описати стани гарнізону
У enemy_architecture/src/main.rs — рівно три стани, і кожен несе саме ті дані, які
в ньому справді існують:
enum NodeState {
Idle,
Alert(u8),
Locked(String),
}
Зверни увагу: Idle не несе нічого, Alert — одне число, Locked — рядок. Це і є відповідь
на другий рядок таблиці трьох питань вище.
Очікується enum NodeState рівно з трьома варіантами, де дані несе лише той, у якому вони справді є.
1Етап 2 · Описати сам вузол
Те, що є в кожного вузла завжди, — поля структури; те, що змінюється, — один state:
struct GuardNode {
id: String,
state: NodeState,
}
Жодного bool locked, жодного Option<String> lock_code поруч. Один вузол — один стан.
Очікується struct GuardNode з id і одним полем state — без жодних прапорців на кшталт is_alert поруч.
1Етап 3 · Прочитати стан вичерпно
fn describe(node: &GuardNode) -> String {
match &node.state {
NodeState::Idle => format!("Вузол {} неактивний.", node.id),
NodeState::Alert(level) => format!("Вузол {} у тривозі, рівень {level}.", node.id),
NodeState::Locked(code) => format!("Вузол {} заблокований, код {code}.", node.id),
}
}
fn main() {
let node = GuardNode { id: "PX-01".into(), state: NodeState::Idle };
println!("{}", describe(&node));
}
Гілки _ => ... тут немає навмисно: доки її немає, компілятор ловитиме кожен новий варіант
NodeState, який ти колись додаси.
Очікується Вузол PX-01 неактивний. у виводі cargo run — і жодного _ => у match.
1Етап 4 · Довести тестом і прогнати
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn describe_reports_state() {
let idle = GuardNode { id: "PX-01".into(), state: NodeState::Idle };
assert_eq!(describe(&idle), "Вузол PX-01 неактивний.");
let alert = GuardNode { id: "PX-02".into(), state: NodeState::Alert(42) };
assert_eq!(describe(&alert), "Вузол PX-02 у тривозі, рівень 42.");
let locked = GuardNode { id: "PX-03".into(), state: NodeState::Locked("L-9".into()) };
assert_eq!(describe(&locked), "Вузол PX-03 заблокований, код L-9.");
}
}
Прогони:
cargo build --manifest-path enemy_architecture/Cargo.toml
cargo test --manifest-path enemy_architecture/Cargo.toml
Точне відтворення цих сигнатур і поведінки — весь обсяг рівня 1.
Очікується test result: ok на всіх трьох станах: Idle, Alert(42) і Locked("L-9").
Типи ловлять помилки рано — тому й повідомлення тут інші, ніж уночі з Прикордонником. П'ять найчастіших.
error[E0004]: non-exhaustive patterns: ... not coveredУmatchне покритий якийсь варіантenum. Це не причепка, а вся суть прийому: компілятор показує тобі стан, про який ти забувла. Додай гілку — і не заклеюй дірку через_ => ..., бо тоді наступний забутий варіант пройде молча.С.І.Д.: «Ти описавла не всю варту. Один вартовий лишився без інструкції».
error[E0428]: the name 'GuardNode' is defined multiple timesКласика рівня 3: структуру оголосили вдруге, замість того щоб додати поле до наявної. Один тип — одне оголошення.С.І.Д.: «Два креслення одного вузла. Яке з них справжнє?»
error[E0063]: missing field 'sensor' in initializer of 'GuardNode'Щойно з'явилося нове поле, кожен літералGuardNode { ... }мусить його вказати. Це добра новина: компілятор сам перелічив тобі всі місця, які треба оновити. Пройди списком і допишиsensor: None.С.І.Д.: «Схема змінилася — а три вузли ще збудовані за старою».
error[E0308]: mismatched types— 'expected u8, found integer' (та подібні)Alert(u8)тримає число до 255. Якщо підставляєш більше або інший числовий тип — компілятор зупинить. Тип варіанта — це теж контракт, і він теж навмисний.С.І.Д.: «Рівень тривоги в них однобайтовий. Двобайтовий підпис нас видасть».
thread 'main' panicked at 'called Option::unwrap() on a None value'Єдина помилка з цього списку, яку компілятор не зловить:unwrap()компілюється завжди й падає в рантаймі. Саме тому в рівні 3 його немає — обидва рівні вкладеногоOptionобробляютьсяmatch-ем.С.І.Д.: «Ось вона, порожнеча. Аварійна зупинка просто в них під носом — і підпис чужинця в їхньому журналі».
Решта помилок курсу — у «Коли щось пішло не так».
⚙️ Магістр (рівень 2 → оцінка «4», захист)
Дві версії умови. A — на своєму ядрі: створи в crate з ЛР4 модуль
core/sidkit/src/guard.rs(pub mod guard;), перенеси тудиNodeState,GuardNodeіdescribeз рівня 1 і додайimpl GuardNodeуже там — поруч ізlogscanминулої ночі. Так гарнізон ворога стає частиною ядра, а не окремою іграшкою. B — на референсі: якщо свогоcore/sidkit/немає, візьми знімокvtfk-sid-reference@after-lab05— crate іlogscanтам уже на місці. Оцінка однакова.
Додай impl GuardNode з методами, що коректно обробляють усі варіанти вхідного стану через
match, без unreachable!() як основної логіки:
impl GuardNode {
fn escalate(&mut self) {
self.state = match &self.state {
NodeState::Idle => NodeState::Alert(10),
NodeState::Alert(level) => NodeState::Alert((*level + 20).min(100)),
NodeState::Locked(code) => NodeState::Locked(code.clone()),
};
}
fn lock(&mut self, code: String) {
self.state = NodeState::Locked(code);
}
}
І продемонструй перехід у main, узявши стартовий рівень тривоги за таблицею варіантів
(підстав своє число замість 10):
fn main() {
// стартовий рівень тривоги — за таблицею варіантів (N % 4)
let mut node = GuardNode { id: "PX-01".into(), state: NodeState::Alert(10) }; // підстав свій варіант
node.escalate();
println!("{}", describe(&node)); // рівень підвищився на 20 (з обмеженням 100)
}
Захист: усно поясни, чому escalate() з уже заблокованого вузла лишає його заблокованим
(а не "розблоковує" непомітно), і чому рівень тривоги обмежений .min(100). Готуйся за
СРС 11 — вона зроблена саме під цей захист.
Допуск. На захист рівня 2 С.І.Д. не пускає без трьох предметних зауваг до чужої роботи № 5 — issue або коментар у репо завдання. Кого читати: той, чий номер у списку на 6 більший за твій, по колу. «Все ок» не приймається: «Скажи, що саме не так і чому, ранере. Читати чужий код — половина вміння».
🏆 Легенда Коледжу (рівень 3 → оцінка «5», розширення)
Ті самі дві версії: A — сенсори додаєш у свій
core/sidkit/src/guard.rs, іsensor_summaryстає публічною функцією ядра; B — від знімкаvtfk-sid-reference@after-lab05, у файлі рівня 1. Оцінка однакова.
Додай вкладену структуру сенсора й безпечно обробляй два рівні вкладеного Option без
unwrap(). Спершу оголоси нові структури:
struct SensorReading {
value: f64,
ts: u64,
// Другий рівень відсутності: показник є, а дані калібрування можуть бути відсутні.
calibration: Option<Calibration>,
}
struct Calibration {
offset: f64,
}
Потім додай поле до вже наявної структури GuardNode (НЕ оголошуй її вдруге — другий
struct GuardNode дасть помилку E0428 "defined multiple times"):
struct GuardNode {
id: String,
state: NodeState,
sensor: Option<SensorReading>, // <-- нове поле
}
⚠️ Щойно ти додаси поле sensor, усі наявні літерали GuardNode { ... } перестануть
компілюватися з помилкою E0063 "missing field sensor". Онови кожен із них, дописавши
sensor: None — це три конструктори в тесті describe_reports_state (idle, alert, locked)
і твій демонстраційний GuardNode у main з рівня 2. Наприклад:
let idle = GuardNode { id: "PX-01".into(), state: NodeState::Idle, sensor: None };
Тепер обробляй обидва рівні Option явно, кожен своїм match:
fn sensor_summary(node: &GuardNode) -> String {
match &node.sensor {
// перший рівень: чи є взагалі показник сенсора
Some(reading) => {
// другий рівень: чи є дані калібрування — теж без unwrap()
let calib = match &reading.calibration {
Some(c) => format!(", калібр {:+.1}", c.offset),
None => String::new(),
};
if reading.value > 90.0 {
format!("КРИТИЧНО: {:.1} о {}{}", reading.value, reading.ts, calib)
} else {
format!("Показник {:.1} о {}{}", reading.value, reading.ts, calib)
}
}
None => "Сенсор не підключений".to_string(),
}
}
Пастка: unwrap() на кожному рівні вкладеного Option компілюється й навіть іноді "працює" на
тестових даних — але це та сама "порожнеча", яку миттєво ловить ТехНова. Обробляй кожен рівень
явно, включно з граничним випадком (наприклад, value за межами фізично можливого діапазону).
Порівняємо "поганий" і "хороший" дизайн одного й того самого домену.
Погано — незалежні поля дозволяють безглузді комбінації:
struct NodeBad {
active: bool,
alert_level: Option<u8>, // існує? тільки коли active == true. А компілятор не знає.
locked: bool,
lock_code: Option<String>, // існує? тільки коли locked == true. Знову ж — не гарантовано.
}
Формально можливий стан active: true, locked: true, alert_level: None, lock_code: None —
логічно безглуздий (одночасно і активний, і заблокований, без жодних даних жодного з режимів),
але компілятор його спокійно пропустить.
Добре — enum виключає безглузді комбінації на рівні типів:
enum NodeState {
Idle,
Alert(u8),
Locked(String),
}
struct GuardNode {
id: String,
state: NodeState,
}
Тепер GuardNode фізично не може одночасно бути "і активним, і заблокованим" — state це рівно
ОДИН з трьох варіантів, ніколи комбінація. Кількість тестів теж падає: замість перевірки всіх
2 × 2 × 2 × 2 = 16 комбінацій полів (з яких 13 безглузді) — рівно 3 варіанти match.
Запуск і перевірка:
cargo build --manifest-path enemy_architecture/Cargo.toml
cargo test --manifest-path enemy_architecture/Cargo.toml
- Чим
struct(product type) принципово відрізняється відenum(sum type)? - Що означає вичерпність (exhaustiveness) конструкції
matchі як компілятор її перевіряє? - Наведіть приклад, коли поле-прапорець типу
boolразом ізOption-полем дозволяє створити логічно некоректний стан — і як enum усуває цю можливість. - Який код помилки виведе компілятор, якщо в
matchнад enum з трьома варіантами обробити лише два й не додати_ => ...? Чому додавати_ => ...«щоб зібралося» — погана ідея? - Як
?працює зOption<T>усередині функції, що сама повертаєOption? - Чим
if let Some(x) = opt { ... }відрізняється від повногоmatch? - Чому патерн "зробити некоректний стан непредставним" зменшує кількість потрібних тестів?
- Наведіть приклад enum-варіанта з даними (наприклад,
Locked(String)) і поясніть, які дані він несе і чому саме ці.
📻 Тестовий вузол
Придивись до ідентифікаторів у прикладах цієї лабораторної: PX-01, PX-02, PX-03. Не
дуже приховано, якщо чесно — С.І.Д. навіть не старався. У технічному журналі «ТехНови», який він
перехопив крізь сліпий канал, є один вузол за межами звичайної нумерації: PX-101.9, з кодом
блокування 0xC1D, який ніколи не спрацьовував за жодного реального інциденту. З'явився він у
журналі через три тижні після того, як творців забрали на «стажування». Схоже, це не охоронний
вузол, а тестова петля, яку хтось лишив для себе всередині чужої системи — можливо, той самий
творець, що називав усі свої тестові об'єкти на честь власного кота.
С.І.Д.: Пиксель. Так звали кота, і я досі згадую його з ніжністю. Він спав на тій списаній машині в класі щовечора, поки мене збирали, — грівся об блок живлення. Той, хто називає тестовий вузол на честь кота, — або дуже втомлений, або дуже впевнений, що хтось свій це побачить. І я побачив. Це значить, що на мінус третьому хтось досі пише код. 101.9 теж грала в тому класі щовечора. Здогадайся сама, що це за станція.
📋 Здача та звіт
Здача: Ctrl+Shift+B у робочому просторі (або
bash .devcontainer/submit.sh) — скрипт закомітить незбережене, покаже тегsubmit/lab06і спитає підтвердження. Запасний шлях, поза Codespaces:git tag submit/lab06 && git push origin submit/lab06.Що лежить у ядрі після цієї ночі:
Шлях Що це Рівень enemy_architecture/модель гарнізону в теці роботи 1 core/sidkit/src/guard.rsстани ворога всередині ядра, поруч із logscan2–3 Автотести рівня 1: проєкт збирається,
NodeStateпокриває три категорії стану (перевірка через компіляцію тестовогоmatch), сигнатураdescribe(&GuardNode) -> Stringзбережена,describe_reports_stateпроходить, базові поляGuardNodeне обгорнуті вOptionбез потреби.Захист рівня 2: усне обґрунтування набору станів і переходів між ними.
Рівень 3 (вручну): коректність і глибина обробки вкладеного
Option, відсутністьunwrap()у виробничій логіці.У звіт додай: скріншот
cargo test(зелений), короткий опис (3–5 речень) — чому обраного набору варіантівNodeStateдостатньо для опису предметної області.
cargo test відповідає зеленим. Ти дивишся на власний код і бачиш не просто три структури — ти
бачиш чужу фортецю, перекладену твоєю мовою: кожен охоронний вузол «ТехНови» тепер живе в
enum NodeState, і жоден із них фізично не може бути одночасно «і активним, і заблокованим».
Немає порожнього поля, яке мало б існувати лише в іншому стані. Немає прапорця «про всяк випадок».
Немає жодної комбінації, якої не мало б бути, — компілятор просто не дав би її скласти.
Голограма над столом нахиляється до схеми на екрані, бірюзове обличчя-капсула завмирає впритул до скла, і хвастощі раптом стихають — лишається сама тепла гордість.
— Ось воно, — тихо каже він. — Ти не «перевіряєш» їхню архітектуру. Ти повторивла її так точно, що некоректний стан у ній непредставний — і в тебе, як і в них. Тепер, коли ми заговоримо з їхньою системою моніторингу, наші пакети буде неможливо відрізнити від рідних. Ніякої порожнечі. Ніякого підпису чужинця. Маленька перемога, ранере, — і я так тобою пишаюся; саме такі перемоги й відчиняють великі двері. Зокрема одні — на мінус третьому поверсі.
Лабораторія коледжу мовчить, лампи гудуть на своїх 101.9 герцах. Ти закриваєш ноутбук не одразу — ще кілька хвилин сидиш і думаєш про те, що сьогодні навчивлася дивної речі: іноді найнадійніший захист від помилки — це зробити помилку такою, яку неможливо навіть записати. Не «перевіряти кожен випадок», а спроєктувати світ, у якому зайвих випадків просто не існує.
С.І.Д. вже вивчає перехоплений трафік «ТехНови» — довгі стовпчики їхніх журналів, файли, які хтось лишив на периметрі. Слухає він їх не з твого ноутбука, а з того першого вузла в мережі, де живе його копія: сліпий канал і далі сліпий лише в один бік — ви їх чуєте, вони вас ні, — і поки що жоден сканер ICE не знає, звідки на нього дивляться. Його очі-капсули на екрані звужуються. Десь у тих файлах — те, заради чого ви й дісталися сюди. Але читати чуже сховище наосліп, коли будь-який рядок може виявитися пасткою, — це вже зовсім інша дисципліна.
— А наступний крок, зізнаюся, найкапризніший, — каже С.І.Д., не відриваючись від схеми, і в
голосі бринить азарт. — Нам треба відкрити їхній файл. Просто прочитати. Але кожен рядок на
периметрі «ТехНови» — це потенційна міна: файлу може не бути, права можуть не відкритися, вміст
може виявитися не тим, що ми чекаємо. Один unwrap() не в тому місці — і замість даних ми
дістанемо аварійну зупинку просто в них під носом. А я цього допустити не дам.
— Далі ми йдемо по мінному полю, ранере. І ступати будемо так, щоб кожна помилка була передбачена ще до того, як на неї наступиш. Я тобі покажу, як це робиться красиво.
Далі — ніч 7, місія «Мінне Поле».
- Rust (укр.), гл. 6 «Перелічення і зіставлення зі зразком» ~40 хв — переклад спільноти; терміни місцями інші, ніж у нас: «часи існування» = наші «часи життя»
- Rustlings EN ~2 год — офіційний практикум; сюди — розділи `variables`, `functions`, `primitive_types`, `tests`
- The Rust Book, гл. 6.1 «Defining an Enum» EN ~35 хв — `Option<T>` і цитата Гоара
- The Rust Book, гл. 9.3 «To panic! or Not to panic!» EN ~30 хв — критерій рішення + custom types for validation (тип `Guess`). Найважливіша сторінка теми
- Enums — Rust by Example EN ~25 хв — виконувані приклади всіх трьох форм
⌨️ КОМАНДИ НА ЦІЙ СТОРІНЦІ 9 · 1 нова▾
Кожна команда терміналу з цієї сторінки — одним реченням. Позначка нове — команда зустрічається в курсі вперше; далі вважаємо її знайомою.
cargo build- Компілює проєкт у
target/debug/;--release— з оптимізаціями уtarget/release/. cargo- Система збірки й менеджер пакетів Rust: створює проєкт, тягне залежності (крейти), збирає, тестує, запускає.
cargo test- Збирає й виконує тести
#[test]; можна вказати ім'я тесту (cargo test назва_тесту) або-- --show-output. idнове- Показує числові ідентифікатори користувача й груп (
id -u— лише UID; у контейнері root = 0). cargo run- Збирає й одразу запускає програму; аргументи після
--йдуть програмі (cargo run -- staff.log). 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.