Бэкенд платформы Lubeno полностью написан на Rust. Он вырос до таких размеров, что я уже не могу удерживать все части его кодовой базы в голове.

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

Выяснилось, что благодаря гарантиям безопасности Rust я гораздо увереннее работаю с кодовой базой. И эта уверенность позволяет мне рефакторить даже критичные части приложения, что крайне положительно влияет на мою продуктивность и удобство поддержки продукта в долгосрочной перспективе.

Сегодня Rust спас меня снова!

Недавно я столкнулся с проблемой, которая заставила меня задуматься, и в конечном итоге стала стимулом к написанию этого поста. Мне нужно было обернуть структуру в мьютекс, потому что доступ к ней выполнялся конкурентно. Для доступа к внутренней структуре нам сначала нужно получить блокировку мьютекса.

let lock = mutex.lock();
// … Заблокированные данные, используются для генерации коммита …
db.insert_commit(commit).await;

Это изменение показалось мне совершенно нормальным, и rust-analyzer со мной согласился. В этом файле не отобразилось никаких ошибок. Но внезапно красным загорелся другой файл в редакторе, указывая на ошибку компиляции в определении router. Мне это показалось полной бессмыслицей: как моя блокировка влияет на выбор обработчика router?

.route("/api/git/post-receive", post(git::post_receive))
                                     ^^^^^^^^^^^^^^^^^
error: future cannot be sent between threads safely
help: within 'impl Future<Output = Result<Response<Body>>', the trait 'Send' is not implemented for "MutexGuard<'_, GitInternal>"

Признаюсь: для того, чтобы разобраться в происходящем, мне понадобилось достаточно много времени. Давайте анализировать вместе!

При поступлении нового HTTP-соединения используемый нами веб-фреймворк порождает для него новую async-задачу. Асинхронные задачи исполняются в планировщике, работающем по принципу work stealing. Это значит, что когда поток завершает всю работу, он начинает «красть» задачи у других потоков, чтобы сбалансировать нагрузку. В Rust это может происходить только в точках «.await».

Есть и ещё одно важное правило — если мьютекс блокируется в одном потоке, то его необходимо освобождать в том же потоке, или возникнет неопределённое поведение.

Rust отслеживает все сроки жизни и знает, что блокировка живёт достаточно долго, поэтому передаёт точку «.await». Это означает, что освобождение блокировки может произойти в другом потоке, а это не допускается, потому что может привести к неопределённому поведению.

Решить проблему очень просто: достаточно освобождать блокировку до «.await».

Подобные баги — самые неприятные! Их почти невозможно отловить при разработке, потому что на этом этапе почти никогда не создаётся нагрузки на систему, достаточной для того, чтобы вынудить планировщик перенести исполнение в другой поток. Так возникают баги типа «невозможно воспроизвести, иногда возникают, но не у нас».

Потрясающе, что компилятор Rust способен выявлять подобные вещи. И что кажущиеся несвязанными такие части языка, как мьютексы, сроки жизни и async-операции образуют такую целостную систему.

С другой стороны, TypeScript пугает

Недавний баг асинхронности в нашей кодовой базе на TypeScript оставался невыявленным ещё долго после того, как код был выпущен в продакшен. Вот виновник бага:

// Пользователь успешно выполнил вход!
if (redirect) {
    window.location.href = redirect;
}

let content = await response.json();
if (content.onboardingDone) {
    window.location.href = "/dashboard";
} else {
    window.location.href = "/onboarding";
}

Всё очень просто. При выполнении входа проверяем, есть ли перенаправление. Если да, то выполняем перенаправление на конкретную страницу. Если нет, переходим к дэшборду или странице онбординга. Присвоение значения «window.location.href» перенаправляет браузер в нужное место.

Я тестировал этот код, и он работал. Но внезапно перестал работать. А работал ли он вообще? Что здесь происходит? Нас всегда перенаправляет на дэшборд, даже если перенаправление есть.

Здесь присутствует состояние гонки планирования. Присвоение значения «window.location.href» не выполняет перенаправление мгновенно, как я предполагал. Оно просто задаёт значение и планирует перенаправление, как только оно станет возможным. Но код продолжает исполняться! Из-за этого следующее присвоение может исполниться до того, как браузер начинает перенаправление, поэтому перенаправляет пользователя в ошибочное место. Для обнаружения причины происходящего мне понадобилась куча времени. Чтобы решить проблему, я просто добавил конструкцию return в блок if, чтобы он никогда не добирался до остальной части кода.

if (redirect) {
    window.location.href = redirect;
    return;
}

Мне кажется, эти возникшие в Rust и TypeScript проблемы схожи. Обе они связаны с async-планированием и обе демонстрируют некое неопределённое поведение, которое не очень-то очевидно. Но проверка типов Rust оказалась гораздо полезнее, благодаря ей баг даже не скомпилировался. Компилятор TypeScript не отслеживает сроки жизни, и у него нет правил заимствования, поэтому он попросту неспособен обнаруживать подобные проблемы.

Рефакторинг без страха

Rust часто рекомендуют как отличный язык для системного программирования, но обычно его не ставят на первое место, когда дело касается веб-приложений. Python, Ruby и JavaScript/Node.js всегда воспринимаются, как более «продуктивные» для веб-разработки. Я же считаю, что это справедливо только для новичков! При работе с этими языками сразу много получаешь «из коробки», поэтому поначалу прогресс очень быстр.

Но когда проект достигает определённого размера, всё начинает пробуксовывать. Между частями кодовой базы присутствует так много слабой связанности, что менять её становится очень сложно.

Все мы с этим сталкивались: меняешь что-то, и всё работает замечательно, но спустя пару дней появляются признаки того, что это изменение поломало другую (совершенно несвязанную) страницу. Когда это случается в третий раз, желание разбираться с кодовой базой начинает стремительно ослабевать.

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

Rust заявляет: «То изменение, которое ты вносишь, влияет на другую часть проекта, о которой ты, возможно, совсем не думал, потому что ты на глубине в шесть вызовов функций, а дедлайн не за горами; но вот конкретные причины, почему это может вызвать проблемы».

А как насчёт тестов?

Я считаю, что тесты — это прекрасно! Это очень мощный инструмент при выполнении крупного рефакторинга, когда нужна помощь в выявлении регрессий. Но компилятор не требует их для исполнения кода, поэтому вы можете просто решить не добавлять тесты.

Иногда работы бывает слишком много, а времени слишком мало. Но с тестами повышается ментальная нагрузка. Мне нужно выбирать подходящий уровень абстракций. Буду ли я тестировать поведение или подробности реализации? Предотвратит ли этот тест все ошибки в будущем? Принимать все эти решения утомительно, к тому же ты часто можешь ошибаться.

Временами понимать и писать код на Rust бывает сложновато, но здорово в нём то, что он снимает с меня бремя принятия решений. Решения были приняты людьми намного умнее меня, работавшими над крупными кодовыми базами и закодировавшими все распространённые ошибки в компилятор.

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

Бонус: Zig тоже пугает!

Zig часто сравнивают с Rust; оба стремятся быть языками системного программирования. Я считаю, что Zig замечательный, но он пугает. Давайте взглянем на простой пример обработки ошибок.

const std = @import("std");

const FileError = error{
    AccessDenied,
};

fn doSomethingThatFails() FileError!void {
    return FileError.AccessDenied;
}

pub fn main() !void {
    doSomethingThatFails() catch |err| {
        if (err == error.AccessDenid) {
            std.debug.print("Access was denied!\n", .{});
        } else {
            std.debug.print("Unexpected error!\n", .{});
        }
    };
}

У нас есть функция «doSomethingThatFails», которая всегда завершается сбоем со значением ошибки «FileError.AccessDenied», после чего мы перехватываем ошибку и выводим, что доступ запрещён.

Только ведь мы этого не делаем. В логике обработки ошибок есть опечатка: «AccessDenid != AccessDenied». Код без проблем скомпилируется. Компилятор Zig сгенерирует по новому числу для каждого уникального «error.*», не беспокоясь о том, какие типы вы сравниваете. Это просто числа.

Однако если использовать оператор «switch» вместо «if», то компилятор Zig внезапно озаботится: «Ой, а тут ведь очевидная ошибка! Возвращаемая ошибка никогда не будет иметь это значение, потому что это не FileError», и откажется компилировать код. Он способен обнаружить баг, но просто не считает важным беспокоиться об этом. Если значение выглядит, как число, оператор «if» вполне может сравнивать его, как число.

Эти мелкие решения языка сильно контрастируют с решениями в Rust. А если вы постоянно допускаете опечатки в именах, то это может пугать.

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


  1. 4ou4
    08.09.2025 14:56

    Сколько времени занимает изучения Rust и нужно ли перед этим знать С?


    1. Fell-x27
      08.09.2025 14:56

      Знание Си в расте скорее мешает. Кучу раз видел как сишники, используя раст, просто продолжают кодить на Си, но в синтаксисе Раста. Компилятор ругается? Ну просто поставим unsafe, и все заработает!

      Си знать не нужно. Но желательно понимать, как работает память компьютера, указатели, стек, куча.

      Для освоения Раста лично для меня бОльшую пользу сыграл Хаскель. Внезапно, да. Но так вышло, что с ним знаком был сильно раньше, а потом оказалось, что в Расте используется много фич, характерных для ФП и Хаскеля в частности.

      На Расте можно писать сишный код, но если писать на Расте именно растовый код, то от си там не будет буквально вообще ничего. Все другое.

      P. S. Мне для вкатывания в Раст на уровень "ну теперь я хотя бы понимаю, что происходит, читаю код и могу писать что-то на начально-среднем уровне" понадобилось меньше недели. Но это зависит от бэкграунда сильно, конечно.


      1. 4ou4
        08.09.2025 14:56

        Торвальдс пишет, что многие пишут на раст ради самого раста то, что проще сделать на С. И что молодежь во главе Раста ведется себя не совсем корректно, чем сами вредят распространению языка.

        Все преимущество это "безопасная модель" памяти, работа с указателями и т.д.
        Это как ООП там, где можно обойтись ФП. В большинстве случаев ООП это глупость, машине все равно, это людям только надо, чтобы сложность упростить.

        Машине глубоко по фиг, что исполнять. Не может быть, чтобы реализацию безопасности с Раста не могла быть перенесена в С без изменения синтаксиса и грамматики языка.

        Это как изучение новых языков, когда людям за 40.
        Люди не учат по той причине, что просто не успеют отбить вложения в изучение нового языка. Изучение ради изучения не имеет смысла.

        Про С++ вообще отдельная тема, создатель Unix говорил в 1980х, что проблема С++ в том, что он так часто меняется, что невозможно расчитывать на то, что написанный код заработает в новой версии языка.

        Вот, гражданин говорит, что ему очень помогло. Но статистика использования языка говорит о том, что торопиться не надо.

        Ежу понятно, для чего создавался С. Чтобы написать UNIX.
        Для чего создавался Rust, объясните. В чем цель создания этого языка?
        Не зная цели языка, можно учить Lisp, Smalltalk или там, Erlang , и тупо потерять время.


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


        1. Fell-x27
          08.09.2025 14:56

          Заглушу холивар на стадии искры.

          Никто у вас не отнимает Си. Никто вам не навязывает Раст. Более того, можно писать на обоих языках.


          1. yrub
            08.09.2025 14:56

            "Никто вам не навязывает Раст. " еще как навязывают ;) постояно везде "раст, раст, раст, это сделано на расте" у нетфликс стек на java (их все устраивает) у них спрашивают "почему не раст"? словно это какой-то грааль программирования. я что-то не припомню чтобы раньше так пиарили какую-то технологию словно это серебряная пуля без каких-либо недостатков


            1. Fell-x27
              08.09.2025 14:56

              словно это какой-то грааль программирования. я что-то не припомню чтобы раньше так пиарили какую-то технологию

              Пайтон, пайтон, пайтон, а почему не на пайтоне пишете? На чем у вас бэкенд? А почему не на пайтоне? А вот пайтон то, а пайтон се, а вот такая-то компания пишет на пайтоне, а вы ещё не пробовали пайтон? Пайтонпайтонпайтонпайтон! /S


              1. yrub
                08.09.2025 14:56

                нет, такого никогда не было, потому что все знали, что пайтан это не про производительность, а следовательно это уже решение с сильным компромиссом. я помню когда руби-рейлс была популярна благодаря скорости разработки (мол топ для стартаперов), потом nodejs за счет возможности держать большое количество соединений, но все равно хайп был лишь отчасти похожий, потому что все (ну или хотя бы большинство) понимали подвохи и возможное целевое использование. но только в случаи с растом все прям специально подчеркивают что "это сделано на расте", практически через черточку после названия продукта ;) когда аналогичное сделано на c++ то так почему-то никто не делает.


            1. Dhwtj
              08.09.2025 14:56

              Почему же мало реальных проектов на раст?

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


        1. vlad4kr7
          08.09.2025 14:56

          Для чего создавался Rust, объясните. В чем цель создания этого языка?

          А может, чтобы заменить яву? Си/++ учить не надо, а получается также быстро.


          1. yrub
            08.09.2025 14:56

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


            1. vlad4kr7
              08.09.2025 14:56

              Да, в расте есть бинарики под разную платформу, но если под musl линукс, то как-бы и не много версий и не надо.

              Например: https://en.wikipedia.org/wiki/Write_once,_run_anywhere

              Сколько с того времени, между прочим 1995 год, сейчас можно запустить?

              А потом 'вжух' и появился докер, и который пытается решать проблемы с версионностью.

              богатой возможностью динамически менять код на лету. ничего этого бинарник дать не может

              динамическая загрузка so/dll и в расте есть.

              кстати - AWS lambda тоже можно на rust

              Поздние связывание сильно поможет в большие проекты, через несколько лет разработки? Вот серьезно - аспекты, аннотации, и прочее оно все может и удобно, но читабельности, понимания иерархии вызовов, и последовательности, особенно при debug, не построчной отладки, a по скудным логам прода, в bugfix нифига не помогает.

              И Dynamic Dispatch, в расте, тоже есть, но не такой популярный.

              В любом случае - если надо чтобы работало быстрее и постоянно, т.е. нет пакетной нагрузки, когда можно дернуть GC, или нельзя ждать на GC локах, то... ну не на Cях-же делать.

              Вам большой проект чего? по строчкам кода или по задаче? если по задаче, есть парочка, тот-же: etcd в виде queue брокер для serverless функций питона из раста. Есть, работает, никто о нем не знает, не потому, что не надо, а потому что не рекламирует его.


              1. yrub
                08.09.2025 14:56

                Сколько с того времени, между прочим 1995 год, сейчас можно запустить?

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

                динамическая загрузка so/dll и в расте есть.

                есть, но как я понимаю только для строго определенных методов, а в java для абсолютно всего. я например как-то делал трюк когда в проприетарном коде мне нужно было изменить поведение, я декомпилировал нужный мне класс (в java с этим тоже нет проблем), поменял нужные строчки, собрал этот 1 класс стандартными консольными средствами, т.к. для компиляции 1 класса по сути ничего не надо кроме его самого и указания на другие уже скомпилированные классы от которых он зависит и вуаля, все подхватилось и все работает. Для нативного кода такой трюк не пройдет.

                Докер не решает проблему на 100%, потому что под арм нужен билд именно под арм, даже если у вас докер, такой вот нюанс.

                Сколько билдов надо и как их собрать для всех комбинаций win-lin-mac x64-arm ? А для IBM мейнфрейма с его архитектурой сможете собрать или для sun sparc в прошлом? Благо, что сейчас это можно в облаке все сделать, но я вот не уверен что у вас не будет какого-то специфического бага в какой-то платформе и мне интересно как вы будете его дебажить не имея оной у себя (хотя может именно для раста это маловероятно, не знаю). Хипдамп тоже можно между платформами анализировать? я сомневаюсь... А в java у вас 1 jar, за все остальное отвечает oracle и таких проблем как правило нет. Как бы не просто так она и подобные решения с vm стали популярны, а потому что у нее есть решение всех этих проблем изначально. 

                Вот серьезно - аспекты, аннотации, и прочее оно все может и удобно, но читабельности, понимания иерархии вызовов, и последовательности, особенно при debug, не построчной отладки, a по скудным логам прода, в bugfix нифига не помогает.

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

                Есть еще кстати любопытный пример: у java есть разные jit, всего таких 4 на рынке но тут интересны 2 от оракла: один на c++ а второй новый на java, который сейчас на 20% быстрее, потому что на java писать проще и она понятней даже для тех, кто разрабатывает jit и как бы сам по себе не дурак. Не знаю на сколько далеко rust от с++ но это как мне кажется хороший пример когда инструмент влияет на результат даже если руки растут из нужного места, может конечно фактор новизны тоже играет роль, но тот на с++ (hotspot) тоже не глупые и не криворукие писали, а по итогу получилось, что крайне мало людей могут разобраться с ним и запилить какие-то новые оптимизации для vm. Так что тут вопрос: а раст точно понятнее, лаконичнее, компактнее и удобнее java чтоб ее заменять? Насколько я слышал например тот же связанный список на чистом расте не сделать.


                1. vlad4kr7
                  08.09.2025 14:56

                  я например как-то делал трюк когда в проприетарном коде

                  а в расте это будут изначально сорцы, которые компилятся с zero cost abstraction.

                  Так что тут вопрос: а раст точно понятнее, лаконичнее, компактнее и удобнее java чтоб ее заменять?

                  ну так попробовать, не?


                  1. yrub
                    08.09.2025 14:56

                    а в расте это будут изначально сорцы, которые компилятся с zero cost abstraction.

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

                    ну так попробовать, не?

                    да, надо будет как-нибудь


                    1. vlad4kr7
                      08.09.2025 14:56

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

                      вот небольшой список либсов и апсов: https://github.com/rust-unofficial/awesome-rust


        1. FirExpl
          08.09.2025 14:56

          Для чего создавался Rust, объясните. В чем цель создания этого языка?

          Чтобы не быть голословным, вот зачем создавался Rust

          https://www.zdnet.com/article/microsoft-70-percent-of-all-security-bugs-are-memory-safety-issues/

          Не может быть, чтобы реализацию безопасности с Раста не могла быть перенесена в С без изменения синтаксиса и грамматики языка.

          Только никто этого не сделал, а Rust вот он с нами


          1. 4ou4
            08.09.2025 14:56

            Специфический use case. Спасибо

            С был написан под UNIX.

            Windows появился спустя 10-15 лет, чтобы выяснилось, что модель памяти не имеет защиты.

            Имея доступ к адресам памяти, можно...

            В основе языка безопасная модель памяти. А комитет по развитию языка С долго реагирует.

            Сколько работы в часах на С , сколько на rust, какая разница в зарплате?

            Вероятно нишевый язык, инициировал msft, ,сама msft массово сокращает программистов.

            Подмножество от подмножества.

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

            А есть ли курс по операционным системам с Rust, порекомендуйте пожалуйста?


            1. domix32
              08.09.2025 14:56

              Rust была придуман Грейдоном Хоаром когда тот ещё работал в Mozilla. Идея была избавить браузер от детских болячек и перестать писать новые. MSFT хотела сделать свой Rust под названием Verona и сделать его типа ультраскалярным, чтобы параллелизовать вычисления (включая сразу на CUDA), но пока в миру его никто не видел и не факт, что проект вообще жив.

              А есть ли курс по операционным системам с Rust, порекомендуйте пожалуйста?

              есть блог о написании своей ОС с нуля, есть девлоги Redox OS, есть книжка по framekernel, есть новостник по OS Dev на ржавчине.


              1. 4ou4
                08.09.2025 14:56

                Спасибо Вам.

                Значит эти и тут, как с браузером и джава. Копируют или покупают.

                подскажите, как на анроид или йос через их терминалы скрипт - фото сделать и, главное, сохранить?

                Через а снимок нельзя сохранить, через й снимок можно только отправить по ip протоколу.

                Надо сделать n снимков, которые глупо и физически невозможно руками сделать. Не подскажите, где найти подобный скрипт? Сидеть и жать руками на экран весь день, это, простите, издевательство.

                Это идеальная задача для cli приложения, а не сидеть весь день строчить рукой по экрану, как обезьяна.

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

                Эти вендоры не дают прав через терминал сохранить сделанную их же телефоном фотку на их же телефон, который вы ошибочно считаете вашим.

                Никакой это не ваш телефон, вы просто арендуете у них эту штуковину.

                Терминала по умолчанию от производителя нет ни в андроиде, ни в йос.

                У производителя есть терминал, но лично для себя, чтобы удалённо управлять их устройством, которое вы ошибочно считаете своим, и вы на купленном своем телефоне даже терминал на запустить не можете, так?


                1. domix32
                  08.09.2025 14:56

                  Значит эти и тут, как с браузером и джава

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

                  подскажите, как на анроид или йос через их терминалы скрипт - фото сделать и, главное, сохранить?

                  совершенно не понимаю о чем вы пытаетесь спросить. надеюсь, что у вас это не последствия инсульта.


                  1. 4ou4
                    08.09.2025 14:56

                    Msft пытались купить предшествееника мозилла, а потом скопировали его, то же и с языком sun Java.

                    Странно, это общеизвестно


                  1. 4ou4
                    08.09.2025 14:56

                    Как через терминал на этих йосандроидах сделать и сохранить фотку туда, куда пальцем можно, не тыкая пальцем: сделай снимок, сохрани снимок.

                    Это г, типа ваш телефон, снимок сделает, а сохранить пишет permission denied. Тогда зачем это недоразумение нужно? Как тренажёр доя тапания по экрану?

                    Печатает так на анроид е. Попробовать после людлома с айфоновсеим терминалом решил, так в андроиде все ещё хуже.

                    Судя по отзывам линуксфоны ещё хуже, но там хотя бы терминал работает.

                    Или линук фон посове уйте, это строчерство с этими тапами реально раздражает


                  1. 4ou4
                    08.09.2025 14:56

                    Сйчас с терминалом все, андроидов нету?

                    Подскажите плиз как через терминал полный доступ к своему андроиду или йос получить


                    1. 4ou4
                      08.09.2025 14:56

                      jailbrake , всем спасибо за ответы


        1. IUIUIUIUIUIUIUI
          08.09.2025 14:56

          Не может быть, чтобы реализацию безопасности с Раста не могла быть перенесена в С без изменения синтаксиса и грамматики языка.

          Как бы вы на C без изменения синтаксиса и грамматики выразили бы «объект по возвращаемому из функции указателю живёт не дольше, чем объект по указателю-параметру этой функции»?

          Как бы вы выразили гарантию «указатель указывает на существующий объект и не является висячим»?


          1. 4ou4
            08.09.2025 14:56

            В виде пакета /библиотеки класса для тех, кому это вообще актуально, и в виде дефаотных политик ктнфигурируеиых для всех остальных , кому нужна тонкая настройка


            1. IUIUIUIUIUIUIUI
              08.09.2025 14:56

              Что за библиотеки класса в C?

              Впрочем, ладно. Не нужно писать реализацию классов/функций/структур этого пакета, опишите просто, пожалуйста, как будет выглядеть сигнатура функции

              const char* substr(const char* str, int pos, int len);

              с использованием такого гипотетического пакета?


              1. 4ou4
                08.09.2025 14:56

                все по Гегелю, если что-то существует, значит, оно кому-то нужно

                Первый курс по специальности был Turbo Pascal, это тип, который создал это язык, смог убедить других, ч то им это надо, но я как на самом курсе не понимал, зачем, так и потом.

                С++ вероятно успешен, потому что Страуструп тратит огромные усилия на его продвижение, до сих пор, 45 лет после создания.

                Кто-то до сих пор поддерживает код, написанный еще до появления С, 1 или 2% этим заняты


    1. yokotoka
      08.09.2025 14:56

      Время зависит от того что вы собираетесь делать. Это как с английским. Сколько времени занимает изучение английского и нужно ли для этого знать кельтский? С Rust всё примерно так же


      1. 4ou4
        08.09.2025 14:56

        спасибо, но английский произошел от немецкого, dane rule и все такое.


        1. yokotoka
          08.09.2025 14:56

          Так и Rust скорее произошёл от Хаскеля, чем от C


          1. Format-X22
            08.09.2025 14:56

            Не помню чтобы я хоть раз писал монаду на Rust, да и мутабельности куча у меня было. Может там и взят какой-то базис, но всё же Rust ближе к ООП чем к функциональному программированию, а то и к процедурному, как раз C. Там даже функции первого порядка только сейчас завозят в nightly-билде, хоть и много иммутабельности и лямбды есть. И всё же это не функциональный язык.


            1. Fell-x27
              08.09.2025 14:56

              Никто не говорил, что Раст - это Хаскель. Монады ему не нужны ровно по той же причине, по которой нужны Хаскелю - by design. Но влияние есть.

              ООП в Расте нет. Там такая же система типов и трейтов как в Хаскеле(typeclasses). Функции высшего порядка есть чуть ли не с первого дня. Функций первого порядка в том же Хаскеле, насколько помню, тоже нет, хотя смотря что вы называете этим термином, конечно. Иммутабельность переменных - дефолтное состояние.

              Раст - не чисто функциональный язык, но отлично работающий в функциональном стиле. И, повторюсь, человеку, знающему Хаскель, Раст будет понятнее, чем человеку, знающему Си. Одни только мэтчи с гвардами чего стоят.

              И тут даже речь не о том, что Раст это ФП, так как это не ФП в чистом виде, а именно в том, чье влияние в нем ощущается больше. От Си там почти и нет ничего.


              1. Format-X22
                08.09.2025 14:56

                Да, с функциями первого порядка - моя ошибка терминологии. Я всегда считал такими не только те функции, которые можно передавать и принимать в параметрах, и возвращать из функций, но и те, которые имеют свои собственные свойства и методы. Однако, судя по данным в интернете, это не обязательно, достаточна возможность их передачи. Впрочем, свойства-методы всё-равно пока в найтли, но их когда-то зарелизят и в стейбл.


                1. Fell-x27
                  08.09.2025 14:56

                  Во, спер из соседней статьи


    1. Format-X22
      08.09.2025 14:56

      Я знал JS/TS, немного Ruby и по мелочи ещё пару языков. Месяца 4 надо чтобы начать писать без боли. Я прочел две онлайн-книги на русском - референс по языку и со стороны практики с примерами. Они предлагаются прямо на сайте раста. Не хватило мне только про async, но в принципе можно почитать спеки Tokio, это библиотека для async. Ещё чтобы не ломало в начале хорошо бы почитать про RefCell и зачем оно, реально вызывало вопросы про передачу данных во множество мест. В принципе через год полноценного изучения можете писать продакшн.


      1. 4ou4
        08.09.2025 14:56

        Спасибо.

        Под какие платформы и какие продукты?

        Вебсервисы на rust пишутся?

        Как мейнтейнится код после сдачи проекта?


      1. 4ou4
        08.09.2025 14:56

        Спасибо Вам.

        Успехов с пользователями.

        Не подскажите, как на анроид через скрипт фото сделать и сохранить?

        Надо сделать n снимков, пальцы сводить начинает. Не подскажите, где найти подобный скрипт?


    1. ArtMan99
      08.09.2025 14:56

      Знать Си не нужно, иногда даже вредно, заставляет думать в неправильных паттернах) А создавался он для простой цели: писать быстрый и безопасный софт, в первую очередь - браузерный движок, чтобы не было тех самых уязвимостей с памятью, которые составляют 70% всех дыр в безопасности


      1. 4ou4
        08.09.2025 14:56

        "Rust была придуман Грейдоном Хоаром когда тот ещё работал в Mozilla. Идея была избавить браузер от детских болячек и перестать писать новые.

        MSFT хотела сделать свой Rust под названием Verona и сделать его типа ультраскалярным, чтобы параллелизовать вычисления, но пока в миру его никто не видел и не факт, что проект вообще жив.

        А есть ли курс по операционным системам с Rust, порекомендуйте пожалуйста?

        есть блог о написании своей ОС с нуля и есть девлоги Redox OS , есть книжка по framekernel , есть новостник по OS Dev на ржавчине."

        "Знать Си не нужно, иногда даже вредно, заставляет думать в неправильных паттернах)

        А создавался он для простой цели: писать быстрый и безопасный софт, в первую очередь - браузерный движок, чтобы не было тех самых уязвимостей с памятью, которые составляют 70% всех дыр в безопасности"

        Спасибо, то есть, создавался он для Mozilla Firefox в 2000х?

        Понимание ниже лежащей UNIX машины спустя 30 лет после создания С очевидно другое, та управляет памятью, выводом и доступом к файлам, и предоставляет терминал для взаимодействия с компьютером. Но большинству хватит в своей жизни даже не С, а простого терминала.

        Проблема в том, что терминал, shell больше не работает на смартфонах, так как они проприетарные.

        Все правильно понимаю?


  1. kolya7k
    08.09.2025 14:56

    Только взглянул на ваш код на TS сразу увидел проблему. С таким сталкивался еще в школе 25 лет назад…

    Пример на Rust как раз показывает всю ущербность Rust. Сам себе создал проблему на ровном месте (await, отсутствие контроля за потоками), сам ее нашел еще и в другом месте и героически не дал собрать проект.

    Напомнило C++ 2000-х годов, где сообщение об ошибке ссылалось на что угодно, только не на строку с проблемой.

    На C++ за десятилетия программирования я ни разу не столкнулся с ошибкой что освободил мьютекс из другого потока…

    Дедлоки - да, но они и в расте могут быть.

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


    1. Dhwtj
      08.09.2025 14:56

      Сам себе создал проблему на ровном месте (await, отсутствие контроля за потоками), сам ее нашел еще и в другом месте и героически не дал собрать проект

      Предлагаете await не использовать совсем? Назад, к лучине, берестяной грамоте. Await на любом языке может неявно менять поток. Но rust об этом скажет, а c++ и др. нет.

      На C++ за десятилетия программирования я ни разу не столкнулся с ошибкой что освободил мьютекс из другого потока

      Короткие локи? Если делать долгие задачи, то обязательно появится


      1. kolya7k
        08.09.2025 14:56

        await на JS не меняет поток :) await в C++ нет. И да, я предпочитаю когда я точно знаю какой поток что выполняет, а не рандом.

        Ради эксперимента можем посоревноваться, я пишу на C++, а вы на Rust и сравниваем скорость разработки, быстродействие программы, безопасность, архитектуру, простоту поддержки и красоту кода. C++ сделает Rust.

        Хороший паттерн блокировок в C++ - scoped_lock, unique_lock, никто уже давно не лочит мьютексы сам. Приходится захватывать мьютекс в одном месте, а освобождать в другом = архитектура кусок говно. Раньше я и без них не делал так, чтобы лок / анлок был в разных функциях


        1. Dhwtj
          08.09.2025 14:56

          можем посоревноваться

          Как в анекдоте: утащите на свой уровень и задавите опытом. Вот, await уже предлагаете не использовать.

          Понятно, что я пишу на раст месяцы, а вы на плюсах десятилетия.

          До этого писал на плюсах, шарп и (прости, Господи, грех страшный, никогда больше так не буду) на пхп.

          Rust очень молодой и незрелый ещё. Тулза только начинает появляться: IDE rustrover хоть можно стало пользоваться с этого лета, не раньше. Библиотек прикладных нет вообще, Excel надо парсить самому если стили надо все прочитать.

          Хороший паттерн блокировок в C++ - scoped_lock, unique_lock, никто уже давно не лочит мьютексы сам

          Но await нету, а аналог такой же как и везде.

          Task<void> fetch() {
              std::cout << std::this_thread::get_id();  // Thread A
          
              auto response = co_await http_get(url);   
              std::cout << std::this_thread::get_id();  // Thread B ???
          
              auto data = co_await process(response);
              std::cout << std::this_thread::get_id();  // Thread C ???
          }

          Не забудьте выстрелить себе в ногу, C++ это любит.


          1. kolya7k
            08.09.2025 14:56

            В C++ нет полноценного “async/await” как законченной модели исполнения. Есть низкоуровневые корутины (co_await/co_yield) - синтаксический сахар без Future/Executor/IO-рантайма, которое нужно всё написать самому, там же и настраивается поведение в каком потоке проснется код после co_await, но тут даже по имени видно "co_await" != "await", о чём я и говорил.
            Я не утверждал, что в C++ нет ничего похожего по поведению на .await в Rust.

            И в любом случае, даже в C++ корутины в продакшене плохо - они делают код некрасивым, сложным, медленным (без шаманств). И даже с этими всеми недостатками гибкость C++ выше на голову.

            В С++ fetch выведет одинаковые thread_id если запустить вот так:

            asio::io_context io;
            
            auto ex = asio::make_strand(io);
            
            co_spawn(ex, fetch(), detached);
            
            io.run();

            В C++ вообще мьютексы на общие данные внутри корутин использовать не очень хорошо, будут проблемы, но тем не менее это можно сделать корректно, например, организовав шардинг задач по id (если вся работа с локами только внутри корутин) или используя await-able мьютексы.

            Выстрелить себе в ногу - это больше про интеллект, чем про ограничения и возможности языка. Тот же нож в руках у идиота тоже может делов натворить и что, из-за пары идиотов делать все ножи тупыми?


            1. 4ou4
              08.09.2025 14:56

              Не подскажите, как на анроид через шелл скрипт фото сделать и сохранить?

              Надо сделать n снимков, пальцы сводить начинает от такого количества нажатий.

              Не подскажите, где найти подобный скрипт? Ни под анжроид, ни под йос ничего нету?


              1. shovdmi
                08.09.2025 14:56

                Вызвать камеру
                adb shell am start -a android.media.action.IMAGE_CAPTURE
                Замените Х и Y на координаты баттона сделать фото
                adb shell input tap X Y
                либо нажать кнопку volume up
                adb shell input keyevent 24

                Список файлов фотографий
                adb shell "ls /sdcard/DCIM/Camera/"

                скачать все фотографии
                adb pull /sdcard/DCIM/Camera/ C:\AndroidPhotos

                скачать одну фотографию
                adb pull /sdcard/DCIM/Camera/IMG_20250911_083000.jpg C:\AndroidPhotos\IMG_20250911_083000.jpg

                На телефоне должен быть включен режим разработчика и в нём отладка
                adb скачайте с https://developer.android.com/tools/releases/platform-tools


                1. 4ou4
                  08.09.2025 14:56

                  спасибо

                  Камеру скрипт открывает, но на IMAGE_CAPTURE, tap и keyevent 24

                  терминал выводит:
                  /system/bin/input : /data/data/com.termux/files/usr/bin/cmd : Permission denied

                  ternux имеет только две записи, права Microphne, Notifications,
                  for Storage, надо отдельно, вероятно, проблема в том, что через termux андроид не дает доступ к файлам

                  у андроида есть терминал на самом смартфоне, по типу termux, чтобы скрипт запустить?


                  how to start terminal on android 15
                  Google :
                  Enable Linux Development Environment:

                  • Navigate to Settings > System > Developer Options.

                  • Scroll down and find the Linux development environment option.

                  • Toggle the switch to the On position. This will enable the Linux environment and initiate the download and setup of necessary components, such as a Debian instance.

                  Но там был экспериментальный терминал, сейчас его нет.



                  Как терминал от андроида 15 запустить, подскажите пожалуйста, с самого андроида, а не через подключения андроида к компьютеру по кабелю USB.

                  Зачем это было усложнять настолько. Это две строки bash в Линукс


                  1. shovdmi
                    08.09.2025 14:56

                    Описанные мной команды необходимо запускать с ПК. Как это повторить из termux я не подскажу


                    1. 4ou4
                      08.09.2025 14:56

                      Теперь понятно, почему RMS глворит, что не пользуется смартфонами. А есть российские смартфоны с российской ОС, в которой терминал работает?


            1. Dhwtj
              08.09.2025 14:56

              Всё-таки, await это удобно.

              Можно, конечно, написать всё руками. Но чаще всего, не нужно.


            1. IUIUIUIUIUIUIUI
              08.09.2025 14:56

              И в любом случае, даже в C++ корутины в продакшене плохо - они делают код некрасивым, сложным, медленным (без шаманств).

              В средней софтине корутины — это очень удобно. Куда проще писать код в духе

              Task<Either<Error, Result>> FetchPhotos(int id)
              {
                const auto& reply = co_await network->request(id);
                const auto& data = co_await *reply;
                const auto& json = co_await parseJSON(data);
                co_return co_await traverseJSON(json, { "reply", "photos" });
              }

              вместо, скажем, Q:

              void FetchPhotos(int id, auto callback)
              {
                auto reply = network->request(id);
                connect(reply,
                        &QNetworkReply::finished,
                        [&]
                        {
                          if (!reply->isValid() || reply->code() != 200)
                          {
                            log() << "bad reply:" << ...;
                            callback(Left { NetworkError {} });
                            return;
                          }
                          const auto& data = reply->data();
                          const auto& maybeJSON = parseJSON(data);
                          if (!maybeJSON)
                          {
                            log() << "failed to parse json" << data;
                            callback(Left { JSONError {} });
                            return;
                          }
                          ...
                        });
              }

              Выстрелить себе в ногу - это больше про интеллект, чем про ограничения и возможности языка. Тот же нож в руках у идиота тоже может делов натворить и что, из-за пары идиотов делать все ножи тупыми?

              Тот же станок в руках у идиота может отрезать ему руку и что, из-за пары идиотов делать раздельные кнопки включения станка и лазерные датчики наличия рук в рабочей зоне?


    1. ArtMan99
      08.09.2025 14:56

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

      Возможно, потому что вы очень опытный разработчик и интуитивно пишете так, чтобы избегать таких ситуаций. А Rust делает это знание доступным даже для новичка, формализуя его в правилах компилятора


      1. Fell-x27
        08.09.2025 14:56

        А еще бывает, что не сталкиваешься с чем-то, потому что не сталкиваешься. Проблема есть, но просто не у тебя в дев среде, а где-то там, у конечного пользователя, который стесняется зарепортить проблему или не понимает, что она есть... Если я не вижу баг, это не значит, что его нет. Просто компиль Раста говорит о проблеме, а компиль С++ нет.


  1. freemorger
    08.09.2025 14:56

    Ну, на моей практике Rust наоборот теряет в эффективности с ростом проекта. Система владения-заимствования уничтожает архитектуру и системы


    1. blind_oracle
      08.09.2025 14:56

      При разработке многопоточных проектов, да и многих прочих, borrow checker часто вообще не видно - 99% лайфтаймов выводятся автоматически, а при шаринге между потоками всё равно используешь Arc и подобные вещи.


    1. Dhwtj
      08.09.2025 14:56

      Это потому что вы пытаетесь передать лайфтаймы через границу модуля. Столкнулся с этим. Так делать нельзя. Разве что случаи, когда вам нужно выжимать последние проценты производительности. Если нет, то просто копируйте, а лучше на границе передавайте DTO. Rust умеет копировать данные быстро.

      Если так сделать, то рост программы будет шёлковым)

      Правда, некоторые контракты лучше зафиксировать на ранней стадии и не менять. Впрочем, так у всех: контракты фронт - бэк не отследить компилятором, контракты / требования потокобезопасности в ООП вообще не видны в сигнатурах ( почти всегда класс не потокобезопасносен, а крайт безопасен, Rust хотя бы Send/Sync показывает). Причем, переход от непотокобезопасного кода к безопасному может устроить вам стрельбу дробью на половину программы.

      Что Rust показывает:

      • Send - можно передать между потоками

      • Sync - можно шарить между потоками

      • !Send - нельзя (Rc, MutexGuard)

      Дедлоки не покажет, да.

      Но можно так

      struct Unlocked;
      struct Locked;
      
      struct Resource<State> {
          phantom: PhantomData<State>
      }
      
      // можно взять лок только в правильном порядке
      impl Resource<Unlocked> {
          fn lock(self) -> Resource<Locked> { ... }
      }

      Или так

      // вместо mutex - каналы
      tx.send(Command::Update(data)).await;


    1. snuk182
      08.09.2025 14:56

      Архитектура явно не учитывала особенности языка. Часто для сложного многомодульного проекта нужно выдохнуть и обмазаться рефселлами / локами на уровне апи, иначе реализация превратится в задачу трех тел.


    1. Fell-x27
      08.09.2025 14:56

      Вероятно, вы не оптимально ее используете. Если писать в ФП-стиле, без сайдэффектов, вам вообще не придётся передавать владение в функции, к примеру. Хватит заимствования рефки немутабельной. Или клонирования, если передаётся объект, который нельзя прочитать, не изменив, по какой-то причине. Ну а если речь идет о чем-то прям совсем одноразовом и не клонируемом, вроде стрима сокета, то и тут владение проблему не создаст...он же одноразовый, после чтения опустошается. Ну передадим его консьюмеру по мутабельной ссылке, значит таков его путь.

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

      Он кажется сиподобным, и это вызывает ложные ассоциации и толкает на применение некорректных паттернов. А на деле там от сиподобности только фигурные скобочки, синтаксис цикла while, и...пожалуй все. Возможно, синтаксис IF, если закрыть глаза на то, что в Расте IF - это выражение. А, ну ссылку можно через звездочку разыменовать.


      1. Nikollor48
        08.09.2025 14:56

        Пожалуй, самый точный и исчерпывающий комментарий в треде

        На Расте надо писать как на Расте

        Все проблемы новичков (и не только) идут от попыток натянуть на него привычные паттерны из C++ или Java


    1. Nikollor48
      08.09.2025 14:56

      А может она не уничтожает архитектуру, а просто выявляет ее недостатки? Если у вас постоянно возникают проблемы с borrow checker-ом, это скорее признак того что у вас слишком запутанные и неявные связи владения данными в системе


  1. ArtMan99
    08.09.2025 14:56

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


  1. jakobz
    08.09.2025 14:56

    А кто понял почему редирект не происходит всегда, сразу после await? Await же всегда отдает промис, и управление отдается браузеру, который сразу должен сделать редирект…


  1. pavel_shabalin
    08.09.2025 14:56

    Присвоение значения «window.location.href» не выполняет перенаправление мгновенно, как я предполагал. Оно просто задаёт значение и планирует перенаправление, как только оно станет возможным.

    А при чём тут typescript? Странно предполагать что присвоение значения переменной моментально прервет текущий поток и запустит некоторую магию. У location ещё location.replace() и location.assign() есть. И всё это АПИ браузера, и ни каким образом это не связано с typescript, JavaScript и уж тем более с await.