Архитектура и бекенд15 мин

Структурата на един проект (3): функцията, която никой не можеше да тества

От · основател и главен изпълнителен директор на Hábil ·

Цялата ти логика е в main.rs и тестовете в tests/ не могат да я импортират, дори с pub. Какво отива в lib.rs, какво остава частно, и шаблон, който го проверява.

Структурата на един проект · част 3 от 7 · Rust

Сценарий: героите и историята са измислени. Техническите данни и цитатите са с източник в края.

За 1 минута

  • В Rust тест в tests/ е друг crate. Той може да ползва само това, което една библиотека обявява за pub. Ако проектът ти има само main.rs, няма какво да се импортира [1].
  • Изходът е програмата да се раздели: логиката в src/lib.rs и тънък main.rs, който само я извиква [2][3].
  • В Rust всичко е частно, докато не кажеш pub [3][5]. Това не е досада: това е граница, която компилаторът пази, и тя ти позволява да променяш вътрешното, без да чупиш никого.
  • Взимаш шаблон, който се клонира и тръгва от първия път, със скрипт, който доказва тази граница, като става червен, когато трябва.
  • За понеделник: потърси в проекта си кои функции от main.rs заслужават тест. Те отиват в библиотеката.

В тази статия

  1. Какво се случи във вторник в 19:20?
  2. Какво иска Rust Book?
  3. Кои са пет чести грешки?
  4. Упражнение: можеш ли да преминеш границата?
  5. Ключови изводи
  6. Какво следва?
  7. Шаблонът и курсът
  8. Какво прави Hábil?
  9. Източници

Какво се случи във вторник в 19:20?

Иван искаше да тества функция отвън, а компилаторът му отговори, че няма какво да се импортира.

Вторник, 19:20. Трети етаж.

Трета задача, първата на Rust. Пак проверителят — този от Go и от Python, който запитва услуги по HTTP. Поръчката: «Отчетът да не показва токените в URL адресите».

Програмата я беше писал някой, който вече го нямаше. Всичко живееше в един-единствен файл:

src/main.rs      900 líneas
Cargo.toml
Cargo.lock

Иван намери функцията, която сглобяваше URL адреса на отчета, около ред 600, и я поправи. Искаше тест, който да казва «токенът не излиза» — от тези, които живеят в tests/. Създаде tests/reporte.rs и написа, както в книгата:

use revisor::url_publica;

cargo test. Отговорът (съкратен):

error[E0432]: unresolved import `revisor`
  = help: if you wanted to use a crate named `revisor`, use `cargo add revisor` to add it to your `Cargo.toml`

Crate на име revisor? Така се казваше проектът му. Сложи pub на функцията и пусна отново. Същата грешка, дума по дума. А подсказката сочеше към външен пакет; той искаше своя.

В 19:41 писа на Лусия, техническата ръководителка.

— Сложих pub и пак не я намира.

— Къде ти е библиотеката? — отговори тя.

— Нямам. Това е програма.

— Ето го проблема. Програмата не експортира нищо. Тестовете ти в tests/ са друг crate, а друг crate може да ползва само библиотека. Утре разделяме този main.rs на две.

Иван погледна 900-те реда. Компилаторът не му казваше, че кодът му е грешен. Казваше му, че няма врата.

Какво иска Rust Book?

Логиката да живее в библиотека, а main.rs само да я извиква, защото само библиотека може да се импортира отвън.

Главата за тестове в Rust Book го казва направо. Ако проектът има само src/main.rs, не можеш да създадеш интеграционни тестове в tests/, които да импортират функциите му, защото «Only library crates expose functions that other crates can use; binary crates are meant to be run on their own» [1]: двоичните файлове са направени да се изпълняват сами.

И дава изхода в същия абзац: прост src/main.rs, който извиква логиката от src/lib.rs [1]. Глава 12 превръща това в рецепта и оставя на main малко: да прочете аргументите, да подготви конфигурацията, да извика библиотеката и да обработи грешката [2].

Измерено на проект с една функция: в main.rs, частна или pub, тестът дава E0432; в lib.rs и pub — минава.

Шаблонът от тази част, estructura-rust, е направен така:

estructura-rust/
├── Cargo.toml · Cargo.lock
├── servicios.yaml           el ejemplo
├── src/
│   ├── main.rs              EL PROGRAMA: opciones, llamada, código de salida
│   ├── lib.rs               LA BIBLIOTECA: declara los módulos y qué es público
│   ├── servicio.rs          el vocabulario. Sin red ni archivos
│   ├── config.rs            lee y valida servicios.yaml
│   ├── revisar.rs           consulta muchos servicios a la vez
│   └── reporte.rs           tabla y JSON
├── tests/                   cada archivo es un crate aparte
│   ├── integracion.rs
│   ├── binario.rs
│   └── comun/mod.rs
├── herramientas/probar-frontera.sh
├── .github/workflows/verificar.yml
└── README.md · LICENSE · LICENSE-CONTENIDO.md

Да минем част по част, с обичайния въпрос: какъв проблем решава днес?

Защо в src/ има два корена?

lib.rs е библиотеката, а main.rs е програмата: два crate-а от един и същ пакет, и Cargo намира и двата, без да го молиш.

Това са пътищата по конвенция на Cargo: библиотеката в src/lib.rs, изпълнимият файл в src/main.rs и интеграционните тестове в tests/ [6]. Затова Cargo.toml не обявява нито един от тях.

Интересното е как си говорят. Програмата е просто още един потребител на библиотеката, като външен crate: «It can only use the public API» [3]. main.rs има същите права като tests/ и като всеки чужд проект, а книгата вижда в това предимство: като си едновременно автор и клиент, проектираш по-добре API-то си [3].

Какво прави тогава main.rs?

Минимумът, който една програма не може да делегира: да прочете опциите, да извика и да реши с какъв код да завърши.

В шаблона main.rs чете три опции, извиква библиотеката, отпечатва отчета и излиза с 0, 1 или 2. Не решава какво е здрава услуга. Останалото се тества, като програмата се изпълни цялата: когато има интеграционни тестове, Cargo компилира програмата предварително и оставя пътя ѝ в CARGO_BIN_EXE_<nombre> [7]. Така работи tests/binario.rs. Това, което е в main.rs, не може да се импортира отвън; да се изпълни — може.

Какво означава, че всичко е частно?

Че нищо не се вижда извън модула, в който се ражда, освен от подмодулите му, докато не му сложиш pub; и компилаторът следи това да се спазва.

Rust Book го казва със списък: в Rust всички елементи (функции, методи, структури, enum-и, модули и константи) «are private to parent modules by default» [3]. Справочникът на езика го обобщава: «By default, everything is private», с две изключения, които тук не са от значение [5].

Защо така? За да знаеш кои части можеш да промениш, без да счупиш външния код, казва книгата [3]. И нарича публичното API на библиотеката ти «your contract with users of your crate» [3]: договорът ти с тези, които я ползват.

Има и средно положение, pub(crate): видимо в целия текущ crate [5]. То подвежда, защото тест в tests/ е друг crate (измерено: дава същата грешка като частното).

В шаблона reporte.rs предлага две публични функции, tabla и json. Тази, която чисти URL адреса, url_publica, е частна: тя е подробност от това как се сглобява отчетът.

Тогава как се тества url_publica?

Отвътре: модулните тестове живеят в същия файл и виждат частното.

Конвенцията на книгата е модул tests в края на всеки файл от src/, отбелязан с #[cfg(test)], за да се компилира само с cargo test [1]. Той е дъщерен подмодул на файла, а «items in child modules can use the items in their ancestor modules» [1]: дъщерният модул вижда частното на предците си. Затова «Rust’s privacy rules do allow you to test private functions» [1]. Разпределението в шаблона:

Тогава как се тества url_publica?
КъдеКакво виждаКакво тества
В края на servicio.rs, config.rs и reporte.rsВсичко, включително частнотоВсяко парче поотделно: url_publica, clasificar, грешките на config
tests/integracion.rsСамо pub от библиотекатаПарчетата заедно, срещу локален HTTP сървър
tests/binario.rsПрограмата, като процесКакво отпечатва и с какъв код излиза
Документацията на servicio.rspubЧе примерът на clasificar продължава да е верен

Последното заслужава един ред. Примерът в коментара на clasificar също е тест: cargo test изважда примерите от документацията на библиотеката и ги изпълнява [8]. Тези на програмата — не: измерено с нарочно грешен пример, в main.rs никой не го пуска и всичко излиза зелено; в lib.rs същият пример става червен. Още една причина логиката да живее в lib.rs.

Ето как изглежда cargo test --locked в шаблона, само редовете, които имат значение (пътищата са съкратени; времената се менят):

     Running unittests src/lib.rs (…)
test result: ok. 28 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
     Running unittests src/main.rs (…)
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
     Running tests/binario.rs (…)
test result: ok. 7 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.33s
     Running tests/integracion.rs (…)
test result: ok. 13 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.13s
   Doc-tests revisor
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

Това са 49 теста в пет блока. Броят се там, в редовете test result, а не по кода на изход: ако файл от tests/ не се компилира, блокът му дори не се появява.

Защо tests/comun/mod.rs, а не tests/comun.rs?

Защото всеки файл в корена на tests/ е отделен набор тестове, а файл с помощни функции би излизал като празен набор при всяко изпълнение.

Двата интеграционни теста споделят тестов HTTP сървър на 127.0.0.1. Ако той живееше в tests/comun.rs, Cargo щеше да го компилира като още един набор [1] и той щеше да излиза при всяко изпълнение с running 0 tests (измерено). Книгата показва този случай и казва, че това «is not what we wanted» [1]. Изходът ѝ е този на шаблона: подпапка, защото файловете в подпапките на tests/ не се компилират като отделни crate-ове [1]. Всеки тест я включва с mod comun;.

А модулите в src/, без mod.rs?

По един файл за модул, с името на модула. Това е пътят, който книгата нарича по-идиоматичен.

Всеки ред pub mod reporte; в lib.rs казва на компилатора да търси src/reporte.rs. Rust Book учи този път и признава и един по-стар, src/reporte/mod.rs, който още работи [4]. Кой е за предпочитане и какво става, ако се смесят, е в грешка 3.

За какво се грижи revisar.rs, което един шаблон не бива да учи погрешно?

Времеви лимит, който покрива цялата заявка, таван за прочетеното и грешки, които никога не повтарят URL адреса.

Времевият лимит обхваща цялата заявка, от името на сървъра до тялото, а от тялото се четат най-много 64 KiB. Една измерена тънкост: ако сървърът за имена не отговаря, отчетът излиза навреме, но програмата приключва чак когато системата се откаже (в Linux — около 10 s). Грешките на reqwest, HTTP библиотеката, «may include the full URL», предупреждава документацията ѝ [12]; шаблонът никога не ползва техния текст. Нито този на грешките от YAML, които повтарят стойността (измерено): казва услугата, полето и, ако YAML файлът не може да се прочете, реда и колоната. Махнахме всяка защита в копие, една по една: и десетте станаха червени. А границите (кодът 400, --paralelo 0, 1 и 60 000 ms) имат свой тест, който става червен, ако границата се премести.

А къде отива поправката на Иван?

В библиотеката, в reporte.rs, до този, който я ползва; а тестът ѝ — в края на същия файл.

В сряда Иван и Лусия разделиха main.rs. Функцията за URL адреса остана в reporte.rs, частна, с модулния си тест до нея. Тестът, който Иван искаше, «токенът не излиза», остана в tests/, но ползва публичната функция json. Отвън се тества обещаното; отвътре — как се изпълнява.

Как разбирам, че границата съществува?

Защото един скрипт я преминава нарочно при всяка промяна и изисква компилаторът да я отхвърли по точната причина.

herramientas/probar-frontera.sh работи върху временно копие. Засажда tests/frontera.rs, тест, който извиква revisor::reporte::url_publica, и го компилира два пъти върху същото копие. Между двата пъти се сменя само една дума в src/reporte.rs — pub:

  1. Контрола, с pub fn url_publica. Тестът трябва да се компилира и да мине. Ако не, експериментът е зле подготвен (функцията е сменила името си, засаденият тест има грешка) и скриптът спира в червено.
  2. Нарушение, със src/reporte.rs такъв, какъвто е. Същият тест трябва да не се компилира, с грешката E0603, в теста, а не в библиотеката.

Ето истинския изход:

1/2 control: con «pub fn url_publica», una prueba de tests/ la llama
   ✅ compiló y pasó (1 de 1): la prueba sembrada está bien escrita
2/2 violación: la misma prueba, con src/reporte.rs tal como está
     error[E0603]: function `url_publica` is private
        --> tests/frontera.rs:6:27
   ✅ el compilador la rechazó: lo que no es pub no se ve desde tests/
✅ frontera: se sostiene

Защо контролата? Защото експеримент, който очаква само червено, става «зелен» и когато се провали по грешната причина. Счупихме го по четири начина: с преименувана функция или зле написан засаден тест спира на «el control NO pasó»; с друг очакван код на грешка казва, че се е провалил, «pero NO por la privacidad»; с функцията като pub казва ❌ COMPILÓ. С pub(crate) границата издържа.

CI в GitHub е написан да пуска форматирането, clippy, тестовете и границата със стабилния Rust и с 1.85.0, а границата — и в macOS със системния /bin/bash, при всяка промяна и всеки понеделник. Шаблонът вече е публикуван в GitHub. Ако той остане 60 дни без активност, GitHub изключва понеделнишкото изпълнение [20].

Кои са пет чести грешки?

Първите две идват от това, че не знаеш какво вижда tests/; третата — от смесването на стилове; последните две — от това, че отваряш повече от нужното: API-то или проекта.

1. Всичко в main.rs, без lib.rs

Работи, но от tests/ нищо не може да се импортира, дори с pub.

Това е грешката на Иван [1]. Глава 12 дава дълбоката причина: тъй като не можеш да тестваш функцията main пряко, изваждането на логиката от нея ти позволява да тестваш цялата логика, а остатъкът се проверява с четене [2]. Издава я E0432, с подсказка за cargo add, която сочи на друго място (измерено). Не ти трябва друг пакет: трябва ти твоята библиотека.

2. Помощните функции за тестове в tests/common.rs

Cargo го компилира като още един набор тестове и той излиза празен при всяко изпълнение.

Книгата го показва: tests/common.rs се появява с running 0 tests, макар да няма тестове [1]. Нищо не чупи, но цапа преброяването. Изходът е подпапката, tests/common/mod.rs [1].

3. Смесване на mod.rs с файлове, които носят името на модула

За един и същ модул компилаторът спира; за различни модули се компилира, но обърква.

Rust Book различава два случая. Ако ползваш двата стила за един и същ модул, «you’ll get a compiler error» [4]. Измерено в шаблона, със src/reporte.rs и src/reporte/mod.rs едновременно:

error[E0761]: file for module `reporte` found at both "src/reporte.rs" and "src/reporte/mod.rs"

При различни модули се компилира, но «might be confusing for people navigating your project» [4], а с mod.rs накрая имаш много еднакво наречени файлове, отворени в редактора [4]. Избери един стил; шаблонът ползва този, който книгата нарича по-идиоматичен.

4. pub на всичко, защото компилаторът се оплаква

Всяко pub разширява това, което другите могат да ползват, и компилаторът престава да те предупреждава, ако е излишно.

Пред E0603 първо се запитай кой има нужда от тази функция. Ако е тест, той отива в края на файла, като модулен тест [1]. Ако е друг модул от същия crate, стига pub(crate) [5].

Ако публикуваш библиотеката или друг пакет я ползва, цената е записана в ръководството за съвместимост на Cargo, което разглежда новите версии на един пакет: «Major: renaming/moving/removing any public items» [11]. Шаблонът не се публикува (publish = false): това, което е pub, се ползва само от main.rs и tests/, които се поправят в същата промяна. Въпреки това има измерена цена: pub изключва предупреждението за мъртъв код. С pub(crate) и без употреба компилаторът казва `warning: function url_publica is never used; с pub мълчи. Затова границата в шаблона става червена, ако url_publica стане pub`.

5. Да не версионираш Cargo.lock, когато CI има нужда от него, или да направиш workspace преждевременно

Първото те оставя без запис с какво е тествано; второто — със структура, за която още няма нужда.

FAQ на Cargo казва, че Cargo.lock записва състоянието на компилация, която е проработила, за да се повтори същата по друго време и на друга машина, и че така CI се проваля само заради нови промени, а не заради външни фактори [9]. cargo new го версионира по подразбиране; FAQ уточнява, че зависи от това, от какво има нужда пакетът ти [9]. Сред употребите посочва една, която шаблонът прави с реда 1.85.0 в CI: «Verifying a minimum-supported Rust version (MSRV) that is less than the latest version of a dependency supports» [9]. И предупреждава за границата: този детерминизъм «can give a false sense of security», защото Cargo.lock не засяга този, който ползва пакета ти [9]. Шаблонът тръгва с --locked, който се проваля, ако файлът липсва или ако cargo иска да го промени [8]. Измерено, без Cargo.lock:

error: cannot create the lock file …/Cargo.lock because --locked was passed to prevent this

Другата крайност е workspace от първия ден: група пакети, които споделят Cargo.lock и папка за компилация [10] и които Rust Book представя за момента, когато проектът вече е пораснал и искаш да го разделиш на няколко crate-а [10]. С един пакет няма какво да се споделя.

5. Да не версионираш Cargo.lock, когато CI има нужда от него, или да направиш workspace преждевременно
ГрешкаКакво я издаваИзточник
1. Всичко в main.rsE0432: unresolved import от tests/[1][2]
2. Помощни функции в tests/common.rsНабор с running 0 tests[1]
3. mod.rs и файл с името на модулаE0761: file for module … found at both[4]
4. pub на всичкоНикакво предупреждение never used; ако публикуваш, всяко премахване е голяма промяна[3][11]
5. Без Cargo.lock при CI, който има нужда от него, или workspace с един пакетcannot create the lock file с --locked; структура, която нищо не ползва[8][9][10]

Упражнение: можеш ли да преминеш границата?

Ще опиташ да ползваш частна функция от tests/, ще видиш колко трябва да отвориш, за да успееш, и ще видиш как скриптът те предупреждава, че си отворил повече от нужното.

Трябват ти Rust 1.85 или по-нов (cargo --version), инсталиран с rustup, плюс Git и bash; около 20 минути, плюс няколко минути първия път, докато се компилират зависимостите.

  • В Linux Mint 22 не ползвай apt install cargo: Mint 22 е базиран на Ubuntu 24.04 [16], който идва с Rust 1.75 [15], а изданието 2024 дойде с 1.85 [18]. Измерено с този cargo: `feature edition2024 is required, плюс предложение да добавиш cargo-features в Cargo.toml. Не го следвай: обнови Rust. Ако вече си го инсталирал с apt, махни го със sudo apt remove cargo rustc; иначе инсталаторът на rustup те предупреждава, че вече има Rust в /usr/bin (измерено). После пусни sudo apt install build-essential curl и командата на rustup` [13][14]:
  curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

Без build-essential излиза `error: linker cc not found` (измерено). Урок 0 от курса по Rust го обяснява стъпка по стъпка. - В macOS — xcode-select --install и после същата команда на rustup [13]. - Ако вече имаш rustup — rustup update stable. В Windows ползвай WSL. - Накрая отвори друг терминал и провери, че cargo --version показва 1.85 или повече: в същия терминал cargo още не се намира или си остава старият (измерено).

Стъпка 1. Клонирай и провери, че всичко е зелено.

git clone https://github.com/HabilMX/estructura-rust
cd estructura-rust
cargo test --locked
bash herramientas/probar-frontera.sh

Какво трябва да видиш: пет реда test result: ok., които дават общо 49 теста, и в края на скрипта ✅ frontera: se sostiene.

Стъпка 2. Предскажи, преди да пипнеш. Ще напишеш тест в tests/, който извиква url_publica, а тя е частна. Запиши три неща: компилира ли се така? А ако я направиш pub(crate)? А ако я направиш pub?

Стъпка 3. Опитай да преминеш.

cat > tests/mia.rs <<'EOF'
#[test]
fn quita_el_token() {
    assert_eq!(
        revisor::reporte::url_publica("https://pagos.ejemplo.com/salud?token=x"),
        "https://pagos.ejemplo.com/salud"
    );
}
EOF
cargo test --locked --test mia

Не се компилира. Първият ред от грешката:

error[E0603]: function `url_publica` is private

Стъпка 4. Отвори я за crate-а. Копирай само реда със sed за твоята система: този за macOS иска празен аргумент след -i (ако на твоя Mac sed е този на GNU, ползвай реда за Linux).

# En Linux:
sed -i 's/^fn url_publica(/pub(crate) fn url_publica(/' src/reporte.rs

# En macOS:
sed -i '' 's/^fn url_publica(/pub(crate) fn url_publica(/' src/reporte.rs

cargo test --locked --test mia

Същото E0603. pub(crate) отваря функцията за целия crate на библиотеката, а твоят тест е друг crate.

Стъпка 5. Отвори я докрай.

# En Linux:
sed -i 's/^pub(crate) fn url_publica(/pub fn url_publica(/' src/reporte.rs

# En macOS:
sed -i '' 's/^pub(crate) fn url_publica(/pub fn url_publica(/' src/reporte.rs

cargo test --locked --test mia
bash herramientas/probar-frontera.sh

Тестът ти минава (test result: ok. 1 passed). Но скриптът става червен:

❌ COMPILÓ: una prueba de tests/ pudo llamar a revisor::reporte::url_publica. La frontera no existe.

Успя да я ползваш отвън и с това я вкара в договора си. Това е контролата в действие.

Стъпка 6. Върни всичко както беше.

git checkout -- . && git clean -fd
bash herramientas/probar-frontera.sh

Внимавай: git clean -fd изтрива и новите файлове, които си създал там, например бележките си.

Как разбираш, че си се справил: пусна седем пъти и видя три резултата. Зелено, четири пъти: двата в стъпка 1, твоят тест в стъпка 5 и границата в стъпка 6. Червено E0603, два пъти: стъпки 3 и 4. И червеното «COMPILÓ» на скрипта в стъпка 5. Ако в стъпка 4 тестът ти се компилира, провери дали не си написал pub вместо pub(crate). Ако в стъпка 5 E0603 остава, редът със sed не е намерил pub(crate) fn url_publica(: прегледай стъпка 4.

За по-нататък (по желание): премести tests/comun/mod.rs в tests/comun.rs (изтрий празната папка) и пусни cargo test --locked. Преди това предскажи колко блока test result ще видиш. Отговор: шест, с един повече от преди, Running tests/comun.rs с running 0 tests (измерено). Върни се със същото git checkout -- . && git clean -fd.

Ключови изводи

Три въпроса, които да зададеш на своето хранилище тази седмица.

  1. Има ли проектът ти src/lib.rs? Ако няма, коя функция от main.rs би искал да тестваш отвън и днес не можеш?
  2. От това, което е pub в библиотеката ти, какво наистина ползва друг crate? Това, което никой не ползва отвън, може ли да е частно или pub(crate)?
  3. Пуска ли се CI-ят ти с --locked? Ако не, с кои версии на зависимостите ти е тестван последният зелен резултат?

Какво следва?

В Go границата я пази инструментът; в Python — инсталацията; в Rust — компилаторът. В TypeScript има една, която редакторът приема, а Node никога не вижда.

Седмица по-късно Иван наследи API на TypeScript. Един import с хубав псевдоним, @/revisar, се компилираше без нито едно оплакване. Редакторът го допълваше сам.

Node, при стартиране, не го намери.

Как може да се компилира програма, която не тръгва? В неделя, 22 ноември, в част 4: какво решава tsconfig.json, какво не решава и защо тестовете и кодът се компилират поотделно.

Шаблонът и курсът

  • Шаблон: github.com/HabilMX/estructura-rust. Използва се като шаблон в GitHub или се клонира [19]. README-то обяснява всяка папка, какво не включва, кога да добавиш всяко нещо и как да махнеш примера. Кодът е под MIT; текстът и обясненията — под CC BY-SA 4.0.
  • Версии: издание 2024 и rust-version = "1.85", първата версия с това издание [18]. Тестван е с Rust 1.99.1, стабилната към 22 октомври [17], в macOS arm64 (и със системния /bin/bash) и в Linux arm64 (контейнер ubuntu:24.04), както и с 1.85.0 в macOS arm64. Linux x86-64 още не е тестван. CI е написан за стабилната версия на всяка седмица и 1.85.0 в Linux, и стабилната в macOS [21]. Зависимостите са фиксирани в Cargo.lock.
  • Курсът по Rust на Hábil: ако модулите, pub или интеграционните тестове са нови за теб, урок 6 ги обяснява от нулата: Курс по Rust · Урок 6, Модули, тестове и Cargo.

Пиша от това, което виждаме всеки ден в Hábil, където изграждаме системи за банки, застраховане и компании в Мексико. Ако в работата или в университета ти и колегите ти попаднете на хранилище, за което никой не знае откъде да го хване, да поговорим: habil.mx

Какво прави Hábil?

Учим на програмиране със същото, с което изграждаме: тестван, обяснен и отворен код.

Тази поредица идва от същото място като нашите курсове. Курсът по Rust е вторият, след Go: учи чии са данните и кои грешки компилаторът хваща преди изпълнение, и го публикуваме отворено, с лиценз Creative Commons; урок 6 обяснява модулите, тестовете и Cargo, които използва шаблонът от тази статия. А в каталога има още курсове по програмиране, безплатни и с тестван и обяснен стъпка по стъпка код.

Разгледай Курса по Rust и каталога Курсове по програмиране.

Ако екипът ти наследи система, за която никой не знае откъде да я хване, или трябва да изгради такава, която да може да се поддържа и занапред, правим и това: разгледай Изграждане на система по поръчка. Поискай първи разговор, без заплащане. В него преглеждаме дали си струва да се настрои това, което екипът ти вече има, да се ползва продукт или да се изгради, и какво би получил екипът във всеки случай. Пиши ни в WhatsApp.

Източниците са проверени на 22 октомври 2026 г. Цитатите на английски са възпроизведени дословно.

Източници

  1. Steve Klabnik, Carol Nichols и Chris Krycho, «Test Organization» (11.3), The Rust Programming Language. https://doc.rust-lang.org/book/ch11-03-test-organization.html
  2. Steve Klabnik, Carol Nichols и Chris Krycho, «Refactoring to Improve Modularity and Error Handling» (12.3): «Separation of Concerns for Binary Projects», The Rust Programming Language. https://doc.rust-lang.org/book/ch12-03-improving-error-handling-and-modularity.html
  3. Steve Klabnik, Carol Nichols и Chris Krycho, «Paths for Referring to an Item in the Module Tree» (7.3), включително «Best Practices for Packages with a Binary and a Library», The Rust Programming Language. https://doc.rust-lang.org/book/ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html
  4. Steve Klabnik, Carol Nichols и Chris Krycho, «Separating Modules into Different Files» (7.5): «Alternate File Paths», The Rust Programming Language. https://doc.rust-lang.org/book/ch07-05-separating-modules-into-different-files.html
  5. The Rust Reference, «Visibility and privacy». https://doc.rust-lang.org/reference/visibility-and-privacy.html
  6. The Cargo Book, «Package Layout». https://doc.rust-lang.org/cargo/guide/project-layout.html
  7. The Cargo Book, «Cargo Targets»: «Binaries» и «Integration tests». https://doc.rust-lang.org/cargo/reference/cargo-targets.html
  8. The Cargo Book, «cargo test»: «Documentation tests» и --locked. https://doc.rust-lang.org/cargo/commands/cargo-test.html
  9. The Cargo Book, «Frequently Asked Questions»: «Why have Cargo.lock in version control?». https://doc.rust-lang.org/cargo/faq.html
  10. Steve Klabnik, Carol Nichols и Chris Krycho, «Cargo Workspaces» (14.3), The Rust Programming Language. https://doc.rust-lang.org/book/ch14-03-cargo-workspaces.html
  11. The Cargo Book, «SemVer Compatibility». https://doc.rust-lang.org/cargo/reference/semver.html
  12. reqwest 0.13.5, «Struct Error», docs.rs. https://docs.rs/reqwest/0.13.5/reqwest/struct.Error.html
  13. Steve Klabnik, Carol Nichols и Chris Krycho, «Installation» (1.1), The Rust Programming Language. https://doc.rust-lang.org/book/ch01-01-installation.html
  14. The Rust Project, «Install Rust», rust-lang.org. https://www.rust-lang.org/tools/install
  15. Launchpad (Canonical), пакет rustc, публикуван в Ubuntu 24.04 «noble»: 1.75.0. https://api.launchpad.net/1.0/ubuntu/+archive/primary?ws.op=getPublishedSources&source_name=rustc&exact_match=true&distro_series=https://api.launchpad.net/1.0/ubuntu/noble
  16. Linux Mint, «Linux Mint 22 Release Notes» («Linux Mint 22.x is based on Ubuntu 24.04»), linuxmint.com. https://www.linuxmint.com/rel_wilma.php
  17. The Rust Release Team, «Announcing Rust 1.99.1», Rust Blog, 15 октомври 2026 г. https://blog.rust-lang.org/2026/10/15/Rust-1.99.1/
  18. The Rust Release Team, «Announcing Rust 1.85.0 and Rust 2024», Rust Blog, 20 февруари 2025 г. https://blog.rust-lang.org/2025/02/20/Rust-1.85.0/
  19. GitHub Docs, «Creating a template repository». https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-template-repository
  20. GitHub Docs, «Disabling and enabling a workflow». https://docs.github.com/en/actions/how-tos/manage-workflow-runs/disable-and-enable-workflows
  21. David Tolnay, «dtolnay/rust-toolchain», README, GitHub. https://github.com/dtolnay/rust-toolchain

Вашата дейност среща ли тези предизвикателства?

Да обсъдим вашия проект

Предпочитате имейл? Пишете ни на hola@habil.mx