Telegram WebApp SDK на Rust: как я полюбил фронтенд

Telegram WebApp (MiniApp) with Rust
Telegram WebApp (MiniApp) with Rust

Предисловие

Это моя первая статья на Хабре. На первый взгляд может показаться, что она хорошо структурирована и грамотна, но не переживайте — Илон Маск до меня не добрался, это не очередной AI-спам.

Просто я такой. Можете зайти в мои open source проекты и посмотреть на код — сами убедитесь. При желании можете связаться и я расскажу про свои загоны. Короче, я педант, люблю красиво. Не надо думать что это "AI spam" — это просто я такой дотошный.

Структура статьи:

  1. Как и зачем я начал делать SDK

  2. Почему Rust на фронтенде имеет смысл

  3. Технические детали и архитектура

  4. Реальный опыт использования

  5. Честные минусы и проблемы

  6. Практические примеры и туториалы

А теперь поехали по тексту.


Как всё началось

Три месяца назад я делал очередной Telegram бот и подумал: "А на *** мне опять тащить npm, React и весь этот зоопарк зависимостей?" У меня бэкенд на Rust, модели данных на Rust, валидаторы на Rust, API на Rust... А фронтенд — на JavaScript. Приходится дублировать типы, поддерживать два разных стека, синхронизировать зависимости.

Это реально бесило.

Я открыл документацию Telegram WebApp API, посмотрел на официальный JS SDK из проекта telegram-apps и решил: "Ладно, попробую обернуть это в Rust через wasm-bindgen. Сколько там может быть? Пара методов?"

Через неделю у меня был рабочий прототип. Ещё через месяц — полноценный SDK на 6300+ строк кода с поддержкой всех методов API. Теперь это опубликованный крейт на crates.io с растущим числом скачиваний.

И знаете что? Это реально работает. Не "работает в теории", не "можно использовать для toy-проектов", а именно работает в продакшене.

Ссылка на проект: github.com/RAprogramm/telegram-webapp-sdk


Зачем вообще Rust на фронтенде?

Все мне говорили:

  • "Мало библиотек, экосистема сырая"

  • "Toolchain тяжелый, сборка долгая"

  • "Всё равно будут мосты с JS — смысла нет"

  • "Developer Experience не такой удобный"

"Sooo, wha’chu gon git, maaan?"

1. Compile-time безопасность

// Это не скомпилируется
let button_color: u32 = "red"; // ❌ error[E0308]: mismatched types

// А это в JavaScript упадёт в runtime
Telegram.WebApp.MainButton.setColor(undefined); // ?

Вызовы API Telegram проверяются на этапе компиляции. Нет места для undefined is not a function, Cannot read property of null, и прочих JS-радостей.

2. Единый стек

Одна и та же структура данных работает и на сервере, и на клиенте. Одна кодовая база, одни валидаторы, один набор тестов. Никаких TypeScript-интерфейсов, которые нужно держать в синхронности с бэкендом.

3. Производительность под контролем

WASM предсказуем. Нет garbage collection пауз, нет "внезапных" тормозов из-за Vue/React reconciliation. Всё детерминировано и измеряемо.

4. Независимость от npm

Помните истории про left-pad, про colors и faker? С Rust крейтами такого не происходит. cargo.io имеет политику immutability — опубликованные версии нельзя удалить или изменить.


Что внутри SDK: цифры и факты

Без воды: 6,284 строк кода, 55 файлов, 2,469 строк документации. Покрывает полностью весь Telegram WebApp API версии 9.2. За 3 месяца вышло 15 релизов.

SDK поддерживает:

  • MainButton, SecondaryButton, BackButton

  • Theme и Viewport управление

  • Cloud Storage (для хранения данных в облаке Telegram)

  • Biometric Authentication

  • Haptic Feedback

  • Device Sensors (акселерометр, гироскоп)

  • Location Manager

  • Invoice Payments

  • И ещё куча всего


Архитектура: как это работает

Я не изобретал велосипед. Я взял официальный API и обернул его в типобезопасную Rust-обёртку через wasm-bindgen.

JavaScript API (Telegram.WebApp)
         ↓
   wasm-bindgen биндинги
         ↓
    Rust API (типобезопасный)
         ↓
   Yew/Leptos интеграция

Модули SDK:

  • telegram_webapp_sdk::webapp — базовые методы, initData, события

  • main_button, secondary_button, back_button — управление кнопками

  • theme, viewport — внешний вид и размеры

  • haptic_feedback — вибрация устройства

  • cloud_storage — хранилище в облаке Telegram

  • biometric — биометрическая аутентификация

  • location_manager, accelerometer, gyroscope — датчики

  • Макросы: telegram_app!, telegram_page!, telegram_router!

  • Интеграции с Yew и Leptos

  • Mock-окружение для локальной разработки


Проблема, которая чуть не убила проект

Самый сложный момент был с обработкой событий. В JavaScript это просто:

Telegram.WebApp.onEvent('mainButtonClicked', () => {
  console.log("Clicked!");
});

Но в Rust через wasm-bindgen нельзя просто так передать замыкание — оно должно жить достаточно долго. Первая версия приводила к segfault при клике на кнопку. Вот это было весело: "Рустовая безопасность памяти", ага.

// ❌ Это НЕ РАБОТАЛО — замыкание умирает сразу
app.on_event("mainButtonClicked", || {
    console::log_1(&"Clicked!".into());
})?;

Проблема в том, что wasm-bindgen требует, чтобы замыкание было 'static. Мне пришлось делать обёртку EventHandle, которая управляет временем жизни через Box::leak() и держит указатель на замыкание.

// ✅ Это РАБОТАЕТ
let handle = app.on_event("mainButtonClicked", |_| {
    console::log_1(&"Clicked!".into());
})?;
// handle автоматически управляет памятью

Эта одна проблема заняла 3 дня дебага с gdb, чтения исходников wasm-bindgen и проклятий в адрес lifetime анализатора. Три. Дня.

Но зато теперь это работает надёжно. И когда я это починил, я понял всю мощь Rust: проблемы жёсткие, но когда решаешь — они решены навсегда. В JS я бы словил этот баг в проде через неделю, потратил бы час на поиски, пофиксил бы... и забыл через месяц, откуда он вообще взялся.


Сравнение: JS SDK vs Rust SDK

Покажу на живом примере. Управление главной кнопкой:

JavaScript (официальный SDK)

Telegram.WebApp.MainButton.setText("Pay $10");
Telegram.WebApp.MainButton.setParams({
  color: "#FF0000",
  text_color: "#FFFFFF"
});
Telegram.WebApp.MainButton.show();
Telegram.WebApp.MainButton.onClick(() => {
  console.log("Processing payment...");
  // тут может прилететь что угодно
});

Rust

use telegram_webapp_sdk::main_button::{set_text, show, on_click, set_params};
use telegram_webapp_sdk::webapp::MainButtonParams;

fn setup_payment_button() {
    set_text("Pay $10").unwrap();
    set_params(&MainButtonParams {
        color: Some("#FF0000".into()),
        text_color: Some("#FFFFFF".into()),
        ..Default::default()
    }).unwrap();
    show().unwrap();

    on_click(|| {
        console::log_1(&"Processing payment...".into());
        // типы проверены на компиляции
    }).unwrap();
}

В JavaScript вы можете передать null, неправильный тип, забыть поле — и узнаете об этом в runtime. В Rust код просто не скомпилируется, если вы передадите что-то неверное.


Интеграция с UI-фреймворками

SDK работает с Yew (React-like) и Leptos (Solid.js-like). Вот как выглядит код на Yew:

use telegram_webapp_sdk::yew::{use_telegram_context, BottomButton};
use yew::prelude::*;

#[function_component(PaymentPage)]
fn payment_page() -> Html {
    let ctx = use_telegram_context().expect("Telegram context");

    html! {
        <div>
            <p>{ format!("User ID: {:?}", ctx.init_data.user) }</p>
            <BottomButton
                text="Pay $10"
                color="#FF0000"
                on_click={Callback::from(|_| { /* обработка */ })}
            />
        </div>
    }
}

Если вы предпочитаете Leptos — он тоже поддерживается из коробки.


Mock окружение для разработки

Одна из проблем разработки Telegram WebApp — нужно запускать приложение внутри Telegram. Дебажить неудобно.

Поэтому я добавил mock-окружение. Создаёте конфиг файл с тестовыми данными пользователя и темы, подключаете фичу mock — и всё, можно запускать приложение локально в браузере без Telegram. Mock автоматически эмулирует весь Telegram.WebApp API.

Это сэкономило мне недели времени на разработку.


Макросы для удобства

Чтобы не писать boilerplate, я добавил макросы telegram_app!, telegram_page! и telegram_router!. Они автоматически инициализируют WebApp, загружают mock в debug-режиме, настраивают роутинг и запускают приложение. Меньше шаблонного кода — больше пользы.


Что не получилось (честно о минусах)

Не всё идеально. Вот с чем я столкнулся:

1. Sourcemaps плохие

В JavaScript у вас отличные sourcemaps, и вы видите точную строку ошибки. В WASM sourcemaps работают плохо. Дебажить сложнее.

Решение: я использую console_error_panic_hook для более читаемых panic-сообщений.

2. Нет Hot Module Replacement

В Vite/Webpack есть HMR — изменения применяются мгновенно. В Rust нужно пересобирать WASM. Это занимает ~5-10 секунд.

Решение: использую trunk с --watch, но это всё равно медленнее чем HMR.

3. Размер бинаря

Даже после оптимизации WASM весит больше чем минифицированный JS. Минимальное Yew приложение ~150KB (gzipped ~50KB) против React ~80KB (gzipped ~30KB).

Решение: для больших приложений разница нивелируется. Плюс есть wasm-opt для дополнительной оптимизации. Но да, "Hello World" на Rust тяжелее.

4. Экосистема UI компонентов

В React есть Material-UI, Ant Design, Chakra. В Rust есть... ну, yew-components и пара библиотек. Всё.

Решение: пишу компоненты сам или использую CSS-фреймворки (Tailwind работает отлично). В итоге даже хорошо — меньше зависимостей, больше контроля.

5. IDE поддержка

rust-analyzer иногда тупит на сложных макросах. Автокомплит не всегда работает идеально.

Решение: терпение и cargo check.


Производительность: цифры

Детальных бенчмарков не делал, но вот что заметил:

  • Сборка prod: React+TS ~45 сек, Rust+Yew ~12 сек

  • Размер бандла: React ~245 KB (gzip), Rust ~156 KB (gzip)

  • Runtime ошибки (первая неделя в проде): React — 3 бага, Rust — 0

Самое главное — все баги ловятся на компиляции, не в проде.


Пример реального использования

Использую SDK в нескольких проектах. Один из них — бот для управления подписками: список активных подписок, оплата через Telegram Invoice API, история транзакций, биометрическая аутентификация для критичных операций.

Стек: Rust везде. Backend (Axum), Frontend (Yew + telegram-webapp-sdk), PostgreSQL, Docker + Fly.io.

Что работает отлично: единые типы между бэком и фронтом, валидация на compile-time, Cloud Storage для кеша, Haptic Feedback для UX.

Что было сложно: настройка CI/CD для WASM, интеграция с Telegram Payments, дебаг в Safari на iOS (sourcemaps бесполезны).


Как начать использовать

Добавляете в Cargo.toml:

telegram-webapp-sdk = { version = "0.2", features = ["yew", "mock"] }

Пишете компонент:

use telegram_webapp_sdk::yew::use_telegram_context;
use yew::prelude::*;

#[function_component(App)]
fn app() -> Html {
    let ctx = use_telegram_context().expect("Telegram context");
    html! {
        <div>
            <h1>{ "Hello, Telegram!" }</h1>
            <p>{ format!("User: {:?}", ctx.init_data.user) }</p>
        </div>
    }
}

Собираете через trunk serve — и всё работает.

Полная документация в README и на docs.rs.


Что дальше?

Продолжаю использовать SDK в продакшене и активно развиваю проект.

Планы:

  • Поддержка Telegram Stars API

  • Улучшить mock-окружение (визуальный конфигуратор)

  • Больше примеров и туториалов

  • Интеграция с Dioxus

Если вы пишете на Rust, делаете Telegram боты, устали от npm-зоопарка и хотите типобезопасность на фронтенде — попробуйте: telegram-webapp-sdk = "0.2"


Ссылки


Заключение

Rust на фронтенде — это не "будущее", это настоящее. Да, экосистема ещё развивается. Да, есть шероховатости. Но если вы хотите типобезопасность, единый стек с бэкендом, предсказуемую производительность и меньше runtime-багов — WASM + Rust это реальная альтернатива JavaScript-стеку.

Я написал SDK, опубликовал его, использую в проде. И это работает. Не в теории, а прямо сейчас.

Если вы дочитали до конца — поздравляю! Значит, AI не вложил в вас дурные мысли о том, что Rust на фронтенде — это безумие ?

Кстати, изначально хотел сделать краткую структурированную статейку, чисто информативную. Но видимо не ценится такое... Поэтому получилось как получилось — с подробностями, примерами и честным рассказом о проблемах.

Просто попробуйте. Предсказуемый фронтенд на Rust — это круто. Возможно, вам зайдёт так же, как зашло мне.

Буду рад PR, issue и просто фидбеку в GitHub репозитории. Особенно если найдёте баги — их точно ещё есть :-)

Спасибо за внимание!

Комментарии (23)


  1. petrov_engineer
    20.10.2025 18:29

    чем pnpm

    pnpm add @telegram-apps/sdk

    отличается от cargo:

    cargo add telegram-webapp-sdk 

    чем typescript:

    const color: number = "red"

    отличается от rust:

    let color: u32 = "red"

    почему баги в логике сравниваются просто циферкой, и на другие неудобные вопросы отвечать не обязательно


    1. petrov_engineer
      20.10.2025 18:29

      И не подумайте, я не иронизирую, есть куча способов даже перенести dto из rust в js, на уровне протокола (например, graphql), генерацией типов (например, specta-rs), 1000 и 1 способ написать клиента, роутинг, фетчинг, стилизацию и тд, и на веб пишут уже десятки лет. Просто перетаскивая одни решения на другие, и называя кастинг void* в типы и структуры безопасным на стадии компиляции, даже не смешно.


      1. RAprogramm Автор
        20.10.2025 18:29

        Да я все эти подходы проходил — генерацию, описания схем, автоклиенты. Это не облегчение, а просто другая форма боли. Иногда проще и надёжнее написать руками, чем потом отлавливать, где генератор решил “догадаться лучше тебя”. Rust тут не про удобство, а про контроль и предсказуемость. Когда ошибка невозможна на уровне типов — это не оптимизация, это выдох.


    1. RAprogramm Автор
      20.10.2025 18:29

      Разница не в языке, а в модели доверия и инвариантах экосистемы.
      Cargo — часть компилятора Rust, а не сторонний тулчейн. Он гарантирует:

      • неизменяемость опубликованных пакетов (immutability policy),

      • воспроизводимые сборки (Cargo.lock обязателен),

      • строгое соблюдение семантического версионирования.

      npm/pnpm — это файловые менеджеры с кэшем. Их можно удалить, подменить, затянуть postinstall-скрипт с майнером. В Cargo это в принципе невозможно: экосистема проектировалась под безопасность и детерминизм, а не “собрать быстрее”.

      В TypeScript типы — соглашение между разработчиками и компилятором. В рантайме их просто нет: можно спокойно скастить всё к any, и проект всё равно соберётся.
      В Rust типы — часть модели исполнения. Компилятор не даст собрать бинарь, если типы не совпадают. Это не подсказка, а жёсткая граница между “работает” и “не существует”.

      ---

      P.S.: Поэтому Rust не “альтернатива React”, а альтернатива категории ошибок, с которыми React-приложения живут.


      1. leaftail1880
        20.10.2025 18:29

        неизменяемость опубликованных пакетов (immutability policy),

        воспроизводимые сборки (Cargo.lock обязателен),

        строгое соблюдение семантического версионирования.

        все это есть и у npm. Инцинденты с вредоносом в пакетах могут произойти только если разработчик сам себе злобный буратино, не коммитит lockfile или использует * в спецификации версии.

        npm/pnpm — это файловые менеджеры с кэшем. Их можно удалить, подменить, затянуть postinstall-скрипт с майнером

        если можно подменить бинарь пакетного менеджера pnpm, то так же можно подменить и cargo. А проверки хэшэй пакетов в pnpm и остальных менеджерах существуют уже давно.

        postinstall скрипты в pnpm по умолчанию вообще выключены и включаются только для выбранных пакетов одной командой.

        можно спокойно скастить всё к any, и проект всё равно соберётся.

        а можно использовать линтер с правилом запрещающим any и не совершать этих ошибок. А если захочется иначе самому себе напакостить, то тут уже никакой раст не поможет.

        Насчет времени компиляции: какой бандлер вы использовали для сравнения? Стоит отметить, что тот же swc, написанный на расте, справится с react приложением быстрее, чем cargo

        Я согласен, что все, что я привел выше, это не стандартные инструменты, ставящиеся отдельно через пакетный менеджер, однако такова экосистема js, в ней есть выбор, какой компилятор использовать и что важнее, скорость или долговечность.

        Также я согласен что в конкретном проекте выбор раста обоснован (генерация типов и моделей между языками это еще большая боль), однако хотелось бы отметить те моменты, которые, на мой взгляд, вы упустили.


        1. RAprogramm Автор
          20.10.2025 18:29

          неизменяемость опубликованных пакетов (immutability policy),
          воспроизводимые сборки (Cargo.lock обязателен),
          строгое соблюдение семантического версионирования.
          все это есть и у npm...

          Формально — частично. Фактически — нет.

          • npm-пакеты можно удалять и перезаливать (такие инциденты были). В Cargo это невозможно: опубликованный crate навсегда иммутабелен, даже автор не может его изменить.

          • npm не гарантирует, что зависимости не будут подменены через зеркала или приватные registry. В Cargo единственный источник — crates.io, хеши пакетов фиксируются в git-индексе.

          • reproducible build в Cargo встроен в сам инструмент. В npm это просто практика, не enforce’ится инфраструктурой.

          если можно подменить бинарь пакетного менеджера pnpm, то так же можно подменить и cargo

          Можно подменить хоть ls, вопрос в вероятности и поверхности атаки.
          Cargo распространяется только через rustup и официальные дистрибутивы с проверкой подписей.
          В экосистеме Node десятки точек входа — node, nvm, n, yarn, pnpm — каждая из которых может стать вектором компрометации.
          Rust экосистема изначально проектировалась под минимизацию таких рисков архитектурно, а не “постфактум” настройками.

          postinstall скрипты в pnpm по умолчанию выключены

          Да, но сама идея выполнения произвольного кода при установке уже компромисс.
          Cargo принципиально не поддерживает никаких установочных скриптов. Это делает целый класс уязвимостей просто невозможным, а не “по умолчанию выключенным”.

          можно использовать линтер с правилом запрещающим any

          Линтер — это совет, не гарантия.
          В Rust типовая корректность — часть модели исполнения. Компилятор не позволит собрать бинарь с несовместимыми типами.
          В TypeScript — можно просто проигнорировать правило и всё равно собрать проект. Это не баг, это философия “надеяться на дисциплину”.

          swc справится быстрее, чем cargo

          Безусловно. Только SWC (тоже написан на Rust, кстати) просто трансформирует AST и собирает бандл.
          Cargo компилирует нативный код, проводит borrow-анализ, линковку и верификацию зависимостей.
          Сравнивать их по скорости — всё равно что мерить, кто “быстрее компилирует”, clang или webpack.

          в JS есть выбор, какой компилятор использовать

          И это и плюс, и минус.
          Свобода выбирать между пятью менеджерами пакетов, четырьмя сборщиками и тремя системами типов — это не богатство, а отсутствие стандарта.
          Rust выбрал противоположное: единый тулчейн, единая модель версионирования, единая точка входа. Меньше гибкости, больше предсказуемости.

          Итого:
          JS-экосистема полагается на дисциплину и самоконтроль.
          Rust — на гарантии, встроенные в компилятор.
          Первое требует внимательности. Второе — просто сборки.


          1. Dhwtj
            20.10.2025 18:29

            В Cargo единственный источник — crates.io

            Очень прикольный момент был когда crates.io был недоступен из России несколько дней / недель

            Есть зеркала, например

            https://rsproxy.cn


            1. RAprogramm Автор
              20.10.2025 18:29

              Да, помню, во Владивостоке находился в один из таких периодов и нормально у меня подгорело


          1. leaftail1880
            20.10.2025 18:29

            Соглашусь почти со всем, однако

            npm-пакеты можно удалять и перезаливать (такие инциденты были).

            Возможно это и были 10 лет назад, но сейчас:

            https://docs.npmjs.com/policies/unpublish

            Registry data is immutable, meaning once published, a package cannot change. We do this for reasons of security and stability of the users who depend on those packages. So if you've ever published a package called "bob" at version 1.1.0, no other package can ever be published with that name at that version. This is true even if that package is unpublished.

            npm не гарантирует, что зависимости не будут подменены через зеркала или приватные registry. В Cargo единственный источник — crates.io, хеши пакетов фиксируются в git-индексе.

            Если вы подключили себе приватные registry, которые могут подменить пакет (по умолчанию все пакетные менеджеры используют registry.npmjs.com), то вы сами себе злобный буратино, по ошибке этого сделать нельзя.

            Хэши пакетов проверяются npm и тоже сохраняются в индексируемый гитом lockfile. При последующем скачивании пакеты устанавливаются с версиями из него. Это не практика, это стандарт, встроенный в инструмент.

            Как я уже говорил, большая часть инциндентов с пакетами произошла из-за публикации новой версии, содержащей вредонос (часто с помощью фишинга, от которого никто не застрахован). От этого не спасает проверка хэшэй или иммутабельность регистри, которые все равно есть, а lockfile, присутствующий и в карго, и в npm, поможет на ура.

            Безусловно. Только SWC (тоже написан на Rust, кстати) просто трансформирует AST и собирает бандл. Cargo компилирует нативный код, проводит borrow-анализ, линковку и верификацию зависимостей. Сравнивать их по скорости — всё равно что мерить, кто “быстрее компилирует”, clang или webpack.

            Тогда почему вы в самой статье приводите это же сравнение, если сами считаете его некорректным?


            1. RAprogramm Автор
              20.10.2025 18:29

              Всё, что вы описали про npm, верно на уровне декларации. Но разница в Rust не в политике, а в инвариантах. Cargo не просто “запрещает unpublish”, он делает невозможной ситуацию, когда система сборки оказывается в непредсказуемом состоянии без прямого участия разработчика. npm лишь регламентирует поведение пользователей, Rust исключает его на уровне механики. Это не “новые правила” — это другой класс гарантий.

              А сравниваю потому, что большинство сравнивает Rust с JS по скорости, и я хотел показать, что это некорректно именно по сути. Не потому что Rust “медленнее”, а потому что у них разные задачи. Компилятор и транспайлер не живут в одной категории.


      1. domix32
        20.10.2025 18:29

        затянуть postinstall-скрипт с майнером.

        вы про build.rs никогда не слышали?


        1. RAprogramm Автор
          20.10.2025 18:29

          В Rust build.rs — единственная точка исполнения, и она не срабатывает при установке зависимости. В npm же postinstall по историческим причинам встроен в модель, и злоумышленники этим реально пользовались (rand-user-agent, eslint-config-prettier, Shai-Hulud). Разница не философская, а архитектурная: в одном случае класс уязвимостей невозможен, в другом — просто “по умолчанию выключен”.


  1. francyfox
    20.10.2025 18:29

    крч, можно было бы писать бэк на nodejs, но тогда бы не было бы SDK на Rust (основная причина написания SDK, а не типизация или избавление от зависимостей). Я заметил ракообразные имеют манию все переписывать на rust, даже там где они не получат особой выгоды. Чтобы утешить свое эго, в конце показывают скорость раста, ВЕДЬ RUST ЭТО БЫСТРО (дууууу... звук из канала mr. teamlead)


    1. RAprogramm Автор
      20.10.2025 18:29

      Да, можно и на ассемблере — только тогда пришлось бы ещё SDK для него написать. Rust тут не ради "скорости", а ради вменяемого API, который не упадёт от первого null.

      Момент одержимости присутствует, но он приносит пользу...


    1. Dhwtj
      20.10.2025 18:29

      Здесь кто-то говорил про скорость?

      Rust хорош для упрощения проектирования (ведь его сигнатуры не врут, а типы это утверждения, доказанные компилятором), для гарантий невозможности некоторых типов ошибок, для лёгкого рефакторинга


  1. vibornoff
    20.10.2025 18:29

    Теперь у нас есть ещё один телеграм-сдк, который автор будет тащить как чемодан без ручки дай бог ещё пару лет. Потом забросит.


    1. RAprogramm Автор
      20.10.2025 18:29

      Да прожить бы дай бог пару лет! Куда уж там ПО поддерживать...


  1. domix32
    20.10.2025 18:29

    55 файлов на 6к строк звучит как боль.


    1. RAprogramm Автор
      20.10.2025 18:29

      Люблю декомпозицию


  1. barloc
    20.10.2025 18:29

    Опять что-то переписали на раст. Вот это да.


    1. RAprogramm Автор
      20.10.2025 18:29

      От следующей статьи у вас волосы дыбом встанут...


    1. qwerty19106
      20.10.2025 18:29

      Переписали - это про конечные программы, утилиту командной строки например.
      А здесь написали что-то новое.


      1. RAprogramm Автор
        20.10.2025 18:29

        О как! Свежее мнение!