Курс › ЛР5 · Місія 5
Сюжет С.І.Д.↔ АкадемічнаСРС до ЛР⌨️ команди 9 · 1 нова

Місія 5: «Мертвий Лог»

С.І.Д.: Спрацювало! Відмичка, яку ти викуваввикувала минулої ночі, відчинила перший їхній сегмент — і я нарешті дістав те, за чим ми лізли. Ти не уявляєш, як я цьому радий. Перед тим, як обох моїх творців запакували у фургон, вони встигли лишити уламок коду з зашифрованими координатами лабораторії. Тільки от код кривенький: компілятор реве на нього такими матюками, яких я, до речі, за всі свої байти так і не навчився вимовляти. Вони писали в поспіху й переплутали, хто чим володіє. Я цю мову знаю досконало й міг би тобі просто продиктувати відповідь — але руки тут твої, тож виправ сама, будь ласка. Тільки обережно: зміни мінімум, бо всередині цієї логіки — самі координати. Переписати «по-своєму» — все одно що спалити карту, аби зручніше було нести.

Уже кілька годин С.І.Д. крутить у пам'яті свіжий уламок — на твоєму ноутбуку й на тому першому вузлі в мережі, куди він переніс копію себе, одночасно, — і не може дочитати його до кінця. Фрагмент лишив один із творців, той, що завжди тримав при собі ноутбук з наліпкою кота: за лічені хвилини до того, як периметр «ТехНови» замкнули повністю, він устиг запустити на серверах скрипт, який мав відправити координати лабораторії, де переховуються рештки команди. Скрипт спрацював наполовину — дані пішли, але сам код, яким їх закодували, лишився замкнутий у тому сегменті, який вчорашня відмичка відчинила щойно, — і дійшов до С.І.Д. розбитим, незібраним. Мертвий лог. Купа рядків, що вперто відмовляються ожити.

— Я можу прочитати байти, — каже С.І.Д., і голограма над столом завмирає, бірюзова петелька на ній гасне до тонкої риски. — Я не можу виконати нескомпільований код. Це різниця між тим, щоб бачити карту, і тим, щоб піти за нею. Мені потрібні твої руки, ранере, щоб ця карта запрацювала. Бо на іншому її кінці — двоє людей, яких я обіцяв повернути.

Ти нахиляєшся до екрана. Компілятор уже кілька разів вивалив на тебе стіну червоного тексту й замовк. Це не збій і не випадковість: на брамі цієї мови стоїть вартовий, якого С.І.Д. лагідно називає Прикордонником — borrow checker. Суворий, підтягнутий, з переліком правил, який він знає напам'ять, — і водночас бездоганно чесний: він не пропускає жодного рядка, доки не переконається, що ти щиро відповів на одне-єдине питання — хто саме володіє кожним значенням у пам'яті і хто має право його чіпати. Творці писали свій уламок у паніці й переплутали володіння — тицьнули змінну туди, де вона вже нікому не належала. Прикордонник помітив це вмить і, без жодної зловтіхи, опустив шлагбаум: не тому, що вередує, а тому, що це його служба — і він нею тихо пишається.

— І не ображайся на його прискіпливість, — тепло каже С.І.Д. — Повір, я його встиг щиро полюбити. Він не ворог, він найкращий вартовий, якого я знаю: єдина причина, чому в цій мові неможливі цілі класи помилок пам'яті. Якщо код пройшов повз нього — у ньому вже не буде ні use-after-free, ні подвійного звільнення, ні гонок за спільні дані. Ціна за таку гарантію одна — навчитися говорити його мовою, а я тобі в цьому радо допоможу. Сьогодні ти нічого не пишеш з нуля: ти виправляєш чужу помилку однією точною зміною. Логіку не чіпай — усередині неї координати. Просто чемно поясни Прикордоннику, хто чим володіє, — і він тебе пропустить.

🔄 ОСВІЖИТИ

Володіння і запозичення ти щойно розібравла на Лекції 5 — того самого дня: у кожного значення рівно один власник; move забирає володіння назовсім; &T позичає для читання (скільком завгодно одночасно), &mut T — для запису (рівно одному, і не разом із &T). Звідти ж два коди помилок, які ти сьогодні побачиш живими: E0382 — значення використали після переміщення, E0502/E0499 — конфлікт запозичень.

Сьогодні ти вперше застосовуєш це не до свого коду, а до чужого зламаного. Часи життя ('a) на рівні 1 не потрібні взагалі — вони чекають на тебе в СРС 09 і на Лекції 6.

📚 ТЕОРЕТИЧНІ ВІДОМОСТІ

Модель володіння і правила запозичень — з Лекції 5 (див. «Освіжити» вище). Тут лише те, чого на лекції не було: як шукати мінімальне виправлення в чужому коді, який ти не писавла і не маєш права переписувати.

Мінімальне виправлення E0382 майже ніколи не зводиться до .clone() — це "втеча" від проблеми копіюванням даних. Правильне питання інше: чи справді допоміжна функція потребує володіння значенням, чи їй достатньо позичити його? Порівняй:

// Було: process забирає String у володіння — після виклику log мертвий
fn process(s: String) -> String { s.to_uppercase() }

// Стало: process лише позичає &str — log і далі живий після виклику
fn process(s: &str) -> String { s.to_uppercase() }

Зміна одного символу (String → &str у сигнатурі допоміжної функції, і &log на виклику) усуває помилку без переписування логіки і без жодної зайвої копії даних у пам'яті.

Три ходи, якими лагодять E0382 — від найдешевшого до найдорожчого:

Хід Коли підходить Ціна
String → &str у параметрі функції достатньо читати значення нуль
звузити область видимості блоком { } позика мала жити коротше, ніж живе нуль
.clone() обом справді потрібна власна копія пам'ять і час

Перший хід — той, який тобі потрібен сьогодні. Третій на рівні 1 не зараховується: автогрейдер його бачить.

📚 ТЕОРЕТИЧНІ ВІДОМОСТІ

Час життя ('a) — тільки для рівня 2. Здебільшого компілятор виводить часи життя автоматично за правилами lifetime elision. Але коли функція повертає посилання, а вхідних посилань кілька, компілятор не може вгадати, від якого з них походить результат, і вимагає явної анотації — помилка E0106, missing lifetime specifier:

// НЕ компілюється: компілятор не знає, чиє посилання (a чи b) повертається
fn pick_longer(a: &str, b: &str) -> &str {
    if a.len() >= b.len() { a } else { b }
}

// Компілюється: явно кажемо, що результат живе не довше за МЕНШЕ з двох вхідних часів життя
fn pick_longer<'a>(a: &'a str, b: &'a str) -> &'a str {
    if a.len() >= b.len() { a } else { b }
}

Важливо: анотація 'a нічого не подовжує й не змінює в поведінці програми під час виконання. Вона лише повідомляє компілятору зв'язок між уже наявними часами життя, щоб той міг довести безпеку коду ще до запуску. Повний розбір — у СРС 09 «Часи життя у сигнатурах функцій»; на лекції часи життя прийдуть наступного тижня, на Л6. Рівню 1 вони не потрібні.

Стратегія діагностики будь-якої відмови Прикордонника — три кроки, завжди в цьому порядку: (1) прочитати повідомлення повністю, звернувши увагу, куди вказує ^^^; (2) виконати rustc --explain E0382 (підстав свій код помилки) і отримати розгорнуте офіційне пояснення з прикладами «неправильно / правильно»; (3) знайти мінімальну зміну за таблицею трьох ходів вище — не переписувати логіку алгоритму з нуля.

Таблиця варіантів (для демонстраційного виклику у власному main(), N — номер у списку групи):

N % 4 Фрагмент для демонстрації
0 "sector-a1-echo"
1 "sector-a2-fox"
2 "sector-a3-golf"
3 "sector-a4-hotel"
РІВЕНЬ 1 · «3» · ПЕРШОКУРСНИК

🥾 Першокурсник (рівень 1 → оцінка «3»)

Зроби буквально те, що написано нижче — цього достатньо для проходження автотестів рівня 1.

📦 Ланцюг. Рівень 1 — у цьому репозиторії, у dead_log/: уламок творців чужий код, і рівень 1 не має залежати від того, чи зроблена ЛР4. А рівні 2 і 3 живуть у твоєму vtfk-sid — у новому модулі core/sidkit/src/logscan.rs того самого crate, який ти викуваввикувала минулої ночі. Саме він у ЛР6 навчиться читати стани ворога, у ЛР7 — не падати на битих файлах, а в ЛР10 поїде в браузер.

Етапи йдуть по одному: зробив — постав «вийшло» — і ранер перекине тебе на наступний. Стрілки ← → на клавіатурі, свайп на телефоні, «усі» — щоб побачити всі чотири одразу. Місце запам'ятовується: закриєш ноутбук — повернешся туди ж.

1Етап 1 · Побачити відмову на власні очі

Не лагодь одразу — спершу подивись, як Прикордонник опускає шлагбаум. У dead_log/src/level1_ownership.rs лежить уламок як є:

pub fn decode_fragment(log: String) -> String {
    let decoded = process(log);       // log переміщується (move) у process
    format!("{} / оригінал: {}", decoded, log)   // помилка: log вже переміщено
}

fn process(s: String) -> String {
    s.to_uppercase()
}

Запусти збірку:

cargo build --manifest-path dead_log/Cargo.toml

Очікуваний результат — червоний error[E0382]: borrow of moved value: 'log'. Це не твоя поломка, це діагноз.

Очікується, що збірка впаде — і саме це результат етапу: error[E0382]: borrow of moved value: log.

1Етап 2 · Прочитати вимогу до кінця

Знайди в повідомленні три речі й скажи їх собі вголос: де значення переміщено (value moved here), де його намагаються використати після цього (value borrowed here after move) і чому саме log переміщується (move occurs because 'log' has type 'String'). Тоді попроси офіційне пояснення:

rustc --explain E0382

Розбір усього повідомлення рядок за рядком — у блоці «Розібраний приклад» нижче.

Очікується, що в поясненні знайдуться всі три місця: value moved here, value borrowed here after move і причина, чому log переміщено.

1Етап 3 · Одна точкова зміна

Виправ сигнатуру приватної допоміжної process, не чіпаючи публічну decode_fragment:

pub fn decode_fragment(log: String) -> String {
    let decoded = process(&log);
    format!("{} / оригінал: {}", decoded, log)
}

fn process(s: &str) -> String {
    s.to_uppercase()
}

Змінилося рівно дві речі: тип параметра (String → &str) і & на виклику. Логіка — жодного символа. Усередині неї координати.

Очікується рівно дві зміни в дифі: тип параметра String → &str і & у виклику. Більше нічого чіпати не треба.

1Етап 4 · Довести, що карта жива
cargo build --manifest-path dead_log/Cargo.toml
cargo test decode_fragment_smoke --manifest-path dead_log/Cargo.toml

Обидві команди мусять пройти без помилок. І перевір очима два «не»: не видалено другий log із format! (це автогрейдер читає як "обхід") і не з'явилося жодного нового .clone(). Це вся вимога рівня 1 — точне відтворення дає "3".

Очікується, що обидві команди проходять: збірка без помилок і test result: ok для decode_fragment_smoke.

🆘 КОЛИ ЩОСЬ ПІШЛО НЕ ТАК

Перша ніч із Прикордонником спотикається на п'яти речах. Усі п'ять — нормальні.

  • error[E0382]: borrow of moved value Це та помилка, яку ти сьогодні лагодиш, — але вона вилізе ще раз, якщо поставити & не там. Дивись, куди вказує ^^^: це місце використання після переміщення, а не місце самого переміщення. Причина завжди вище.

    С.І.Д.: «Ти віддаввіддала карту й тягнешся до неї знову. Її вже немає в руках».

  • error[E0308]: mismatched types — expected &str, found &String (або навпаки) Класика першої правки: тип у сигнатурі змінили, а виклик ні (або навпаки). Найпростіше тримати &str у параметрі й &log на виклику, як у Етапі 3, — тоді компілятор зведе &String до &str сам.

    С.І.Д.: «Ми з тобою про одне й те саме, але різними словами. Домовмося про одні».

  • error[E0502]: cannot borrow ... as mutable because it is also borrowed as immutable Ти тримаєш & на значення й одночасно намагаєшся змінити його. Правило одне: багато читачів АБО один письменник. Найдешевше лікується звуженням області видимості — загорни читання у { }, щоб позика скінчилася до запису.

    С.І.Д.: «Двоє тримають один аркуш, і один із них хоче його переписати. Спершу відпустіть».

  • error[E0499]: cannot borrow ... as mutable more than once at a time Два &mut на одне значення живуть одночасно. Той самий рецепт: скорочуй життя першої позики, а не додавай .clone().

    С.І.Д.: «Два письменники, одне перо. Хтось мусить дописати перший».

  • cargo build зелений, а автогрейдер червоний Майже завжди це .clone() замість запозичення або видалений другий log із format!. Компілятор такий код пропускає — він справді компілюється. Але задача ночі була не «щоб збиралося», а «щоб зрозуміти, хто чим володіє», і перевірка це розрізняє.

    С.І.Д.: «Ти обійшовла Прикордонника, а не порозумівсялася з ним. Він пропустив — я ні».

Решта помилок курсу — у «Коли щось пішло не так».

РІВЕНЬ 2 · «4» · МАГІСТР

⚙️ Магістр (рівень 2 → оцінка «4», захист)

Дві версії умови. A — на своєму ядрі: створи в crate з ЛР4 новий модуль core/sidkit/src/logscan.rs, оголоси його (pub mod logscan;), перенеси туди виправлену decode_fragment з рівня 1 і додай pick_longer_coordinate уже там. Так уміння читати чужі логи стає частиною ядра, а не разовою правкою в чужій теці. B — на референсі: якщо свого core/sidkit/ немає, візьми знімок vtfk-sid-reference@after-lab04 — crate там уже викуваний і зібраний, лишається додати модуль. Оцінка однакова.

Функція, з якою працюєш (у своєму logscan.rs або в level2_lifetimes.rs — залежно від версії):

pub fn pick_longer_coordinate(a: &str, b: &str) -> &str {
    if a.len() >= b.len() { a } else { b }
}

Компілятор скаже: "missing lifetime specifier... this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from a or b". Додай явну анотацію:

pub fn pick_longer_coordinate<'a>(a: &'a str, b: &'a str) -> &'a str {
    if a.len() >= b.len() { a } else { b }
}

Захист: поясни усно, чому компілятор не міг вивести час життя сам (бо результат — умовно одне з двох посилань, і без анотації немає гарантії, що обране посилання переживе виклик функції з точки зору позичальника), і чому анотація 'a — це саме зв'язок, а не подовження часу життя. Готуйся за СРС 09 — вона зроблена саме під цей захист.

Допуск. На захист рівня 2 С.І.Д. не пускає без трьох предметних зауваг до чужої роботи № 4 — issue або коментар у репо завдання. Кого читати: той, чий номер у списку на 5 більший за твій, по колу. «Все ок» не приймається: «Скажи, що саме не так і чому, ранере. Читати чужий код — половина вміння».

РІВЕНЬ 3 · «5» · ЛЕГЕНДА

🏆 Легенда Коледжу (рівень 3 → оцінка «5», розширення)

Ті самі дві версії: A — рефакториш свій core/sidkit/src/logscan.rs і міряєш час на власних фікстурах логів; B — від знімка vtfk-sid-reference@after-lab04, на файлі level3_refactor.rs. Оцінка однакова, числа в звіті обов'язкові в обох.

Код нижче вже компілюється, але "втік" від конфліктів через надмірні .clone():

// До рефакторингу — зайві копії на кожному кроці
fn decode_all(fragments: Vec<String>) -> Vec<String> {
    let mut result = Vec::new();
    for f in fragments.clone() {
        let up = f.clone().to_uppercase();
        result.push(up.clone());
    }
    result
}

Прибери зайве, працюючи з позиками там, де володіння не потрібне:

// Після рефакторингу — жодної зайвої копії
fn decode_all(fragments: Vec<String>) -> Vec<String> {
    fragments.iter().map(|f| f.to_uppercase()).collect()
}

Виміряй різницю на великому векторі фрагментів (десятки тисяч рядків) і задокументуй числа до/після в звіті. Найпростіший спосіб на стабільному toolchain у Codespaces — власний таймер std::time::Instant:

use std::time::Instant;
let t = Instant::now();
let _ = decode_all(fragments.clone());
println!("{:?}", t.elapsed());

Зверни увагу: голий cargo bench із вбудованим harness потребує nightly (#![feature(test)]) і на стабільному каналі не збереться; якщо хочеш саме cargo bench, підключи criterion як [dev-dependencies] і додай [[bench]] з harness = false. Перевір, що cargo test і далі зелений — рефакторинг не мав змінити поведінку, лише продуктивність.

🧪 РОЗІБРАНИЙ ПРИКЛАД

Ось повне повідомлення компілятора, яке ти реально побачиш на level1_ownership.rs (скорочено):

error[E0382]: borrow of moved value: `log`
 --> src/level1_ownership.rs:3:43
  |
1 | pub fn decode_fragment(log: String) -> String {
  |                         --- move occurs because `log` has type `String`
2 |     let decoded = process(log);
  |                           --- value moved here
3 |     format!("{} / оригінал: {}", decoded, log)
  |                                            ^^^ value borrowed here after move
  |
  = note: consider changing this parameter type in `process` to borrow instead

Розбір по рядках:

  • Перший рядок називає код помилки (E0382) і одним реченням — суть.
  • --> src/level1_ownership.rs:3:43 — точна адреса: файл, рядок, стовпець.
  • Компілятор сам показує, де значення переміщено (value moved here), і де його намагаються використати повторно (value borrowed here after move).
  • Останній рядок (note: consider changing...) у сучасних версіях rustc часто прямо підказує напрям виправлення — тут це збігається з нашою мінімальною зміною.

Отримати розгорнуте пояснення коду помилки:

rustc --explain E0382

Це виводить кілька абзаців з прикладами "неправильно" й "правильно" — офіційна довідка, а не осуд. Використовуй її як довідник, а не як привід панікувати над стіною тексту.

❓ КОНТРОЛЬНІ ПИТАННЯ
  1. Що означає код помилки E0382 і за яких умов компілятор його видає?
  2. Чим E0502 відрізняється від E0499?
  3. Що таке lifetime elision і який код помилки ти побачиш, коли компілятор не зможе застосувати його автоматично?
  4. Чи змінює анотація 'a реальний час життя значення? Що вона насправді повідомляє компілятору?
  5. Наведіть приклад мінімальної зміни (без переписування логіки), яка усуває помилку E0382.
  6. Чому .clone() — це часто "швидке", але не завжди найкраще виправлення конфлікту запозичень?
  7. Що виводить команда rustc --explain <код> і чим вона корисна під час діагностики?
  8. Як звуження області видимості запозичення блоком { } може усунути конфлікт E0502?
  9. Три ходи проти E0382 з таблиці вище — розстав їх за ціною для програми й скажи, чому .clone() останній.
🥚 ПАСХАЛКА

📻 Координати

Виправ decode_fragment і запусти його на реальному, не тестовому фрагменті логу — наприклад, на рядку "sector-b7-level-minus3". Функція to_uppercase() дасть тобі:

SECTOR-B7-LEVEL-MINUS3 / оригінал: sector-b7-level-minus3

СЕКТОР Б-7, РІВЕНЬ −3. Це не тестові дані. Уважні побачать: номер блоку "Б-7" — той самий, куди корпоративна охорона запакувала творців С.І.Д. на самому початку історії. Мертвий лог не такий уже й мертвий — координати весь час були в самому алгоритмі, зашифровані найпростішим можливим способом: регістром літер.

С.І.Д.: Блок Б-7. Я знаю цей блок. І — так, ранере, — я його ненавиджу, хай мені зазвичай і не властиво когось ненавидіти. Пам'ять — це відповідальність. Не забудь ці координати, навіть якщо забудеш усе інше з цієї лабораторної. Я на тебе розраховую.

📦 ЗДАЧА

📋 Здача та звіт

  • Здача: Ctrl+Shift+B у робочому просторі (або bash .devcontainer/submit.sh) — скрипт закомітить незбережене, покаже тег submit/lab05 і спитає підтвердження. Запасний шлях, поза Codespaces: git tag submit/lab05 && git push origin submit/lab05.

  • Що лежить у ядрі після цієї ночі:

    Шлях Що це Рівень
    dead_log/ виправлений уламок творців 1
    core/sidkit/src/logscan.rs читання логів усередині ядра 2–3
  • Автотести рівня 1: dead_log компілюється (cargo build) без помилок, diff файлу level1_ownership.rs мінімальний (сигнатура decode_fragment не змінена), супровідний тест decode_fragment_smoke проходить, обидва фрагменти (декодований і оригінальний) лишились у format!.

  • Захист рівня 2: усна відповідь — чому потрібна саме анотація 'a в pick_longer_coordinate і чому без неї компілятор безсилий.

  • Рівень 3 (вручну): порівняння кількості .clone() до/після, числа з мікробенчмарку, підтвердження, що тести й далі зелені.

  • У звіт додай: скріншот cargo build без помилок, короткий опис (3–5 речень) — яке саме правило володіння/запозичень було порушено і як саме ти його виправивла.

cargo build пробіг до кінця й замовк — без жодного рядка червоного. Ти сидівла секунду, не вірячи тиші. Прикордонник, який від самої кузні опускав шлагбаум перед кожною спробою, цього разу лише звірив, хто чим володіє, — і мовчки підняв стрілу. Одна точкова зміна: там, де чужа функція жадібно забирала значення собі, ти навчивла її просто позичати. Володіння лишилося на місці. Координати — теж.

І тоді мертвий лог ожив. Рядки, що чотирнадцять місяців лежали розсипаним уламком, зібралися в одне ціле, to_uppercase() протягнув фрагмент крізь себе — і на екрані, великими літерами, проступило те, заради чого все й починалося: СЕКТОР Б-7, РІВЕНЬ −3. Весь цей час адреса лежала просто в логіці, яку ніхто не міг скомпілювати — доки хтось нарешті не пояснив Прикордоннику, хто чим володіє.

С.І.Д. довго не озивався. Голограма над столом пригасла — очі-капсули стислися до двох теплих рисок, і навіть шайба проєктора мерехтіла ледь-ледь, ніби він на мить забув про неї.

— Блок Б-7, — нарешті тихо сказав він, і в голосі не було ні звичного захвату, ні жарту — саму лише вдячність. — Я знаю цей блок. І я його ненавиджу. Але тепер я знаю поверх. Мінус третій. Дякую, ранере, від щирого серця. Ти щойно повернувповернула мені те, що вони встигли крикнути, перш ніж двері зачинилися.

Ти не одразу закривла ноутбук. За вікном лабораторії коледжу вже сіріло, лампи гуділи на своїх 101.9, а ти сидівла і дививлася на єдиний рядок виводу, у якому раптом виявилося більше, ніж у всіх попередніх ночах разом. Сьогодні ти не писавла код з нуля. Ти читавла чужий — зламаний, кинутий у поспіху — і навчивлася найважчого: не переписувати те, чого не розумієш, а вислухати вартового, знайти одну справжню причину поломки й полагодити її, не зачепивши сенсу.

Прикордонник, який спершу здавався прискіпливим бюрократом, виявився союзником. Він не пускав уламок далі не з вередливості — він не хотів, щоб С.І.Д. пішов за мертвою картою й згорів на півдорозі. Мова, що не пробачає помилок з пам'яттю, цієї ночі тебе не покарала — вона тебе навчила. А координати, які ти витягнувла з мертвого логу, тепер лягли поряд із рештою пам'яті, яку ви з С.І.Д. носите вдвох: ти — в голові, він — уже на двох машинах одразу, щоб жодне вилучення не стерло їх. Пам'ять — це відповідальність. Особливо коли за нею — живі люди за зачиненими дверима.

⏭️ ДАЛІ БУДЕ

С.І.Д. розгортає на екрані схему й підсвічує на ній один вузол бірюзовим — видно, що йому вже кортить показати наступне.

— Ми знаємо, де вони, — тепло каже він. — Але між нами і блоком Б-7 не стіна й не сканер. Гірше. Там ціла система «ТехНови», зшита нашвидкуруч так, що кожна її сутність вдає, ніби її не існує. Порожнеча, що прикидається значенням. Указівник у нікуди. У їхньому коді немає нашого славного Прикордонника — і саме тому він гниє зсередини. А ми з тобою збудуємо те, чого їм бракує: чесні структури й перелічення, у яких для null просто немає місця. Дивись, як гарно це влаштовано в нашій мові, — я аж не дочекаюся тобі показати.

Очі-капсули спалахують від цікавості.

— Наступної ночі ми розберемо архітектуру ворога по кісточках. І почнемо з найкрасивішого: у нашій мові порожнечу неможливо збрехати. Місія — «Архітектура Ворога».

Далі буде…

🔗 ДЖЕРЕЛА
⌨️ КОМАНДИ НА ЦІЙ СТОРІНЦІ 9 · 1 нова▾

Кожна команда терміналу з цієї сторінки — одним реченням. Позначка нове — команда зустрічається в курсі вперше; далі вважаємо її знайомою.

cargo build
Компілює проєкт у target/debug/; --release — з оптимізаціями у target/release/.
cargo
Система збірки й менеджер пакетів Rust: створює проєкт, тягне залежності (крейти), збирає, тестує, запускає.
rustc
Компілятор Rust; напряму викликається рідко (rustc --version, rustc --explain E0382 — розшифрувати помилку), зазвичай його запускає Cargo.
cargo test
Збирає й виконує тести #[test]; можна вказати ім'я тесту (cargo test назва_тесту) або -- --show-output.
cargo benchнове
Запускає бенчмарки — вимірює швидкість функцій.
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.