Comments 25
чем pnpm
pnpm add @telegram-apps/sdkотличается от cargo:
cargo add telegram-webapp-sdk чем typescript:
const color: number = "red"отличается от rust:
let color: u32 = "red"почему баги в логике сравниваются просто циферкой, и на другие неудобные вопросы отвечать не обязательно
И не подумайте, я не иронизирую, есть куча способов даже перенести dto из rust в js, на уровне протокола (например, graphql), генерацией типов (например, specta-rs), 1000 и 1 способ написать клиента, роутинг, фетчинг, стилизацию и тд, и на веб пишут уже десятки лет. Просто перетаскивая одни решения на другие, и называя кастинг void* в типы и структуры безопасным на стадии компиляции, даже не смешно.
Да я все эти подходы проходил — генерацию, описания схем, автоклиенты. Это не облегчение, а просто другая форма боли. Иногда проще и надёжнее написать руками, чем потом отлавливать, где генератор решил “догадаться лучше тебя”. Rust тут не про удобство, а про контроль и предсказуемость. Когда ошибка невозможна на уровне типов — это не оптимизация, это выдох.
Разница не в языке, а в модели доверия и инвариантах экосистемы.
Cargo — часть компилятора Rust, а не сторонний тулчейн. Он гарантирует:
неизменяемость опубликованных пакетов (immutability policy),
воспроизводимые сборки (Cargo.lock обязателен),
строгое соблюдение семантического версионирования.
npm/pnpm — это файловые менеджеры с кэшем. Их можно удалить, подменить, затянуть postinstall-скрипт с майнером. В Cargo это в принципе невозможно: экосистема проектировалась под безопасность и детерминизм, а не “собрать быстрее”.
В TypeScript типы — соглашение между разработчиками и компилятором. В рантайме их просто нет: можно спокойно скастить всё к any, и проект всё равно соберётся.
В Rust типы — часть модели исполнения. Компилятор не даст собрать бинарь, если типы не совпадают. Это не подсказка, а жёсткая граница между “работает” и “не существует”.
---
P.S.: Поэтому Rust не “альтернатива React”, а альтернатива категории ошибок, с которыми React-приложения живут.
неизменяемость опубликованных пакетов (immutability policy),
воспроизводимые сборки (Cargo.lock обязателен),
строгое соблюдение семантического версионирования.
все это есть и у npm. Инцинденты с вредоносом в пакетах могут произойти только если разработчик сам себе злобный буратино, не коммитит lockfile или использует * в спецификации версии.
npm/pnpm — это файловые менеджеры с кэшем. Их можно удалить, подменить, затянуть postinstall-скрипт с майнером
если можно подменить бинарь пакетного менеджера pnpm, то так же можно подменить и cargo. А проверки хэшэй пакетов в pnpm и остальных менеджерах существуют уже давно.
postinstall скрипты в pnpm по умолчанию вообще выключены и включаются только для выбранных пакетов одной командой.
можно спокойно скастить всё к
any, и проект всё равно соберётся.
а можно использовать линтер с правилом запрещающим any и не совершать этих ошибок. А если захочется иначе самому себе напакостить, то тут уже никакой раст не поможет.
Насчет времени компиляции: какой бандлер вы использовали для сравнения? Стоит отметить, что тот же swc, написанный на расте, справится с react приложением быстрее, чем cargo
Я согласен, что все, что я привел выше, это не стандартные инструменты, ставящиеся отдельно через пакетный менеджер, однако такова экосистема js, в ней есть выбор, какой компилятор использовать и что важнее, скорость или долговечность.
Также я согласен что в конкретном проекте выбор раста обоснован (генерация типов и моделей между языками это еще большая боль), однако хотелось бы отметить те моменты, которые, на мой взгляд, вы упустили.
неизменяемость опубликованных пакетов (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 — на гарантии, встроенные в компилятор.
Первое требует внимательности. Второе — просто сборки.
В Cargo единственный источник — crates.io
Очень прикольный момент был когда crates.io был недоступен из России несколько дней / недель
Есть зеркала, например
Соглашусь почти со всем, однако
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.
Тогда почему вы в самой статье приводите это же сравнение, если сами считаете его некорректным?
Всё, что вы описали про npm, верно на уровне декларации. Но разница в Rust не в политике, а в инвариантах. Cargo не просто “запрещает unpublish”, он делает невозможной ситуацию, когда система сборки оказывается в непредсказуемом состоянии без прямого участия разработчика. npm лишь регламентирует поведение пользователей, Rust исключает его на уровне механики. Это не “новые правила” — это другой класс гарантий.
А сравниваю потому, что большинство сравнивает Rust с JS по скорости, и я хотел показать, что это некорректно именно по сути. Не потому что Rust “медленнее”, а потому что у них разные задачи. Компилятор и транспайлер не живут в одной категории.
затянуть postinstall-скрипт с майнером.
вы про build.rs никогда не слышали?
В Rust build.rs — единственная точка исполнения, и она не срабатывает при установке зависимости. В npm же postinstall по историческим причинам встроен в модель, и злоумышленники этим реально пользовались (rand-user-agent, eslint-config-prettier, Shai-Hulud). Разница не философская, а архитектурная: в одном случае класс уязвимостей невозможен, в другом — просто “по умолчанию выключен”.
крч, можно было бы писать бэк на nodejs, но тогда бы не было бы SDK на Rust (основная причина написания SDK, а не типизация или избавление от зависимостей). Я заметил ракообразные имеют манию все переписывать на rust, даже там где они не получат особой выгоды. Чтобы утешить свое эго, в конце показывают скорость раста, ВЕДЬ RUST ЭТО БЫСТРО (дууууу... звук из канала mr. teamlead)
Да, можно и на ассемблере — только тогда пришлось бы ещё SDK для него написать. Rust тут не ради "скорости", а ради вменяемого API, который не упадёт от первого null.
Момент одержимости присутствует, но он приносит пользу...
Здесь кто-то говорил про скорость?
Rust хорош для упрощения проектирования (ведь его сигнатуры не врут, а типы это утверждения, доказанные компилятором), для гарантий невозможности некоторых типов ошибок, для лёгкого рефакторинга
Теперь у нас есть ещё один телеграм-сдк, который автор будет тащить как чемодан без ручки дай бог ещё пару лет. Потом забросит.
55 файлов на 6к строк звучит как боль.
Опять что-то переписали на раст. Вот это да.
Нуу... Если уж это обычный wrapper с учётом перехода на Rust, то он должен быть:
удобным (типобезопасным, с минимумом макросов; меньше строк — значит лучше);
и, конечно же, производительным.
Поэтому единственное, что можно раскрыть в статье — это архитектура и различные оптимизационные трюки, которые были применены.
Rust вместо React: как я написал Telegram WebApp SDK на Rust