Ржавеем дальше. Как появился Rust и можно ли на нём WEB?

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

    В комментариях я встретил много вопросов типа «А можно ли использовать rust для WEB?» Лаконичный ответ таков: «Можно». Можно и brainfuck использовать, если хочется. Нужно ли? Скажем так, brainfuck для WEB использовать категорически не стоит. А вот rust – тут надо понимать что именно делает rust и каковы его цели. Для того чтобы это понять мы должны погрузиться в компилятор и разобраться в устройстве процессоров. Под катом вы найдёте глубокий заныр в историю того как появился rust и поймёте что это такое и когда его нужно использовать а когда можно и на «ноде запилить».

    Акт номер 0, Вступление

    Ок. Мне довелось обучать программистов вот уже как 10 лет. Я видел разный народ. Кто-то приходил ко мне с вопросами о том, как компилятор оптимизирует код с поддержкой MMX в процессоре, а кто-то спрашивал, можно ли скомпилировать код на Java в С#. Разница между первыми и вторыми – это понимание того как работает процессор.

    В этот момент большинство из тех кто учился программировать по ютубу просматривая видео из серии «Как скачать генератор to-do list на node.js, React, brew, JSX, Pug, LESS за 10 секунд» начинают убегать. Не бойтесь. Я попытаюсь всё объяснить по-человечески. Конечно, есть на хабре и те, кто могут открыть бинарный файл в HEX, посмотреть на 7а 45 4с 46 01 01 01 00 и сказать: «Да этож линуксовский бинарник!» Таким не обязательно читать дальше.

    Акт номер 1: Процессор

    Всё начинается с процессора. Когда вы достаёте себе новый Intel Core i9 из коробки и смотрите на инструкции которые к нему прилагаются, вы найдёте пару страниц описания того как запилить новый камень на маму. Но вот настоящие инструкции к процессору вы в коробке не найдёте.

    На сайте Intel вы всегда можете слить свежую копию мануала. Качайте на здоровье, он бесплатный. В нём 50 мегабайт и 5000 страниц. Ого. Ага. Процессор—это достаточно сложное устройство. Это только часть мануалов по процессору. Скачать можно больше.

    Хорошо, заставить вас прочитать книгу в 5000 страниц будет не просто. Я буду говорить упрощениями.

    Что такое процессор? Это компонент который выполняет арифметические, логические и другие команды ввода-вывода. Процессор это основной компонент в вашем компьютере.

    Как заставить процессор выполнить команду? Передать процессору на вход код этой команды и параметры, которые вам нужны.

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

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

    В дополнение ко всему этому процессор может запросить данные из оперативной памяти. С точки зрения процессора память очень медленная. Посему можно запросить данные из памяти и поместить их в кеш. (Которого на современных процессорах от 2х до 32х+ мегабайт). Кеш он быстрее оперативной памяти, но всё же медленный.

    Хорошо, мы можем получать данные, изменять их и сохранять их в памяти. Если мне надо выполнить одну операцию сложения в каждой ячейке памяти в объёме 1 мегабайт, то есть это я должен выполнить эти команды (1x1024x1024) 1,048,576 раз? Да. Миллион раз. Процессор, работающий на частоте 2 гигагерца выполняет примерно 2,000,000,000 команд в секунду. То бишь, это миллион раз ему можно сделать 2000 раз в секунду. Неплохо.

    Но со временем задачи становятся более и более сложными, и процессоры усложняются с этими задачами. Что если мне надо сделать эту операцию миллиард раз в секунду? Например современная игра в разрешении 4к выводит на экран 33,177,600 пикселов. Это надо выводить 60 раз в секунду. 33,177,600x60 = 1,990,656,000. Хаха. Это почти два миллиарда пикселов в секунду. А для того чтобы всё это обработать потребуется чуть больше чем просто прибавлять и отнимать значения из ячеек регистров. (На самом деле с видеоиграми всё становится ещё интереснее, потому что нынче над этой картинкой будет работать не только процессор, но и, понятное дело, видеокарта, которая содержит в себе кучу этих процессоров).

    Ладно, вопрос понятен, как сделать так, чтобы процессор выполнял ещё больше инструкций в одну единицу времени? Создатели процессоров начали добавлять различные примочки типа SSE. Это набор инструкций который ускоряет воспроизведение видео. (Вернее ускорял. SEE это древняя технология. Сейчас, в большинстве случаев, видео занимается видеочип. Но SEE остались). Что можно делать с этими инструкциями? Ну, например, можно запихнуть сразу 16 значений в определённые 16 регистров и выполнить одну команду на всех 16ти за один такт. Удобно.

    Современные процессоры имеют дополнительные модули для работы с видео, шифрованием, нейронными сетями и так далее. Таких примочек – пруд пруди. Посему и мануал на 5000 страниц.

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

    На помощь приходят прерывания. Это специальные команды процессора, которые позволяют остановить выполнение программы и заняться чем-нибудь другим. Есть на хабре замечательная статья «О работе ПК на примере Windows 10 и клавиатуры». Эта статья в мельчайших деталях рассказывает о том, как сигнал от клавиатуры попадает в память где до него может добраться процессор. По такому же методу процессор может записать данные в видеопамять, и видеоадаптер нарисует эти данные на экране.

    Всё просто, чики-пуки, правильно? Ага. Мы только что видели опкоды команд для одного кристалла Intel. А есть ещё, например, процессоры компании AMD. У них основной набор команд практически такой-же как у Intel, но вот примочки у них были свои. А есть ещё и другие процессоры, в которых набор комманд полностью отличается от того, что мы видели в Intel. Пример тому – мобильные процессоры. У них и архитектура другая.

    То есть, если ты хочешь писать программу на опкодах, то тебе её придётся писать для каждого процессора, на котором эта программа будет запускаться. Что, удобно? Не очень. Да, это достаточно ресурсоёмко. Сидеть и запоминать эти опкоды и пихать их в процессор? Неужели кто-нибудь может запомнить всё это? (Я могу поспорить, что определённый процент хабравчан тут усмехнётся и начнёт вспоминать эти опкоды).

    Акт номер 2: Ассемблер

    Ладно, не боись. Всё это решили за тебя ещё в древности. Уже в 60х годах знали, как с этим разобраться. Для начала, почему бы не заменить численные опкоды чем-то более человекочитаемым? Например, использовать mov вместо 0x06. Так появился первый ассемблер. Программа на ассемблере выглядит вот так:

    INC COUNT        ; Increment the memory variable COUNT
    
    MOV TOTAL, 48    ; Transfer the value 48 in the 
                     ; memory variable TOTAL
    
    ADD AH, BH       ; Add the content of the 
                     ; BH register into the AH register
    
    AND MASK1, 128   ; Perform AND operation on the 
                     ; variable MASK1 and 128
    
    ADD MARKS, 10    ; Add 10 to the variable MARKS
    MOV AL, 10       ; Transfer the value 10 to the AL 

    Ещё одна полезная фича компилятора это оптимизация вашего кода под определённый процессор. Например, если компилятор собирает программу под Intel, то он может автоматически заменить более простые и медленные конструкции, которые ты использовал в своей программе, более сложными командами под процессор Intel или AMD или что ты там используешь.

    Основной плюс такого подхода заключался в том, что одна и та же программа написанная на ассемблере могла быть собрана для разных процессоров. То есть тебе не надо было всё переписывать заново. Победа!

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

    Акт номер 3: Сиииии и Сипипииии

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

     cmp    ax, bx      
     jl     Less  
     mov    word [X], 1    
     jmp    Both            
    Less: 
     mov    word [X], -1  
    Both:

    Вот этим

    if (i > 5)  
    {
      X = 1; 
    }  
    else 
    { 
      X = -1; 
    }

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

    После всего этого мы передаём нашу текстовую программу компилятору, который собирает эту программу в объектный файл. Этот объектный файл передаётся linker (линкеру), который прикручивает ваш код куда надо.

    Так, стоп, а это откуда здесь взялось? Куда чего надо прикручивать? Так, ты в своём коде использовал printf? Да, использовал. Код printf это часть стандартного набора команд. Этот код – функция, которая была скомпилирована в отдельный объектный файл. Когда ты эту функцию вызываешь надо чтобы твой программный код заставил процессор прервать выполение твоей программы, выяснить, где в памяти находится printf, выставить регистр исполняемой команды процессора в определённое значение и вызвать нужное прерывание, чтобы процессор бросил твою команду и ринулся выполнять printf. После выполнения процессор должен вернуться к твоей программе. Опять прыгание по памяти и всё такое.

    Писать всё это руками было бы очень муторно, но линкер решает эти проблемы. Он собирает все объектные файлы (куски исполняемой программы) в один большой исполняемый бинарник. Выясняет все имена функций и проставляет все адреса в памяти как надо. Тобишь всё складывается воедино линкером и он выдаёт тебе в руки один исполняемый файл. Ты его клик-клик и программа запускается!

    Магия.

    Чем же так хорош С? Тем что он был достаточно прост, стандартен и позволял быстро писать программы не заморачиваясь тем, как работает ваш процессор. Компиляторов (обычно под этим понимается компилятор+линкер) для С было написано столько, что ныне их уже не счесть. С всегда был стандартным языком. В нём точно говорится, что если в программе написано if то в итоге код должен делать такое-то ветвление. (А если в программе написано봎볈볬, то твой код должен выпускать демонов из ноздрей.) Посему, если вы написали программу для своего любимого, на тот момент компьютера PDP-11, то её можно было бы скомпилировать и на нынешнем Intel Core i11.

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

    И компиляторы были и есть. Их в С более 50ти популярных. Просто утопия. Типа того. На самом деле, не всё ТАК уж прям утопично. Код, написанный для 32х битных систем может скомпилироваться, а может и не скомипилироваться на 64х битных системах. А вот код который дёргает 64х битные функции точно рухнет на 32х битных системах. Это ещё что. Windows, unix и MacOS имеют разные подсистемы управления памятью. Что-то может заработать на маке и выдать замечательное приветствие на винде:

    Ну, эти проблемы в С решили. У нас есть система под названием makefile. Это программный комплекс который управляет процессом компиляции. Когда файлов у вас в проекте сотни и последовательность компилирования может меняться, когда надо сделать так чтобы система собиралась по-разному на Windows и Unix или проверяла, если сборка делается на 64х разрядном компьютере, вы пишите makefile.

    Всё это становится очень утомительным. В добавок ко всему, С хорош в написании функций, но вот мы-то работаем с объектами. Посему пришлось писать улучшенную версию С, которая называется С++. Плюсы позволили создавать тръу ООП программы с классами, бллк-джеком и куртизанками. (Уж простите меня на ужасное утрирование. Разница между С и С++ достойна не то что отдельной статьи, можно будет и книгу написать. Но статья-то про rust, так что мы будем опускать детали.)

    Акт номер 4: Ява и дотнет

    В конце концов к середине 80х жизнь стала очень сложной, потому что всё это дело стало ну совсем уж непереносимым. Компьютеров становилось больше, системы становились разнее и разнее. Ладно, почему бы не применить другой подход? Давайте сделаем так, чтобы ваш код не компилировался под процессор вообще! Давайте скомпилируем его в какой-то виртуальный код, который мы будем запускать с помощью нашего программного обеспечения. Так у нас появились Java (1995), а после него и .net (2002).

    Код, который вы пишите на этих языках компилируется в промежуточный язык. В .net этот язык называется CIL (Common Intermediate Language, стандартный промежуточный язык). Этот язык выдаёт что-то похожее на ассемблер, но достаточно далёкое от ассемблера. Посмотрите сами:

    .class private auto ansi beforefieldinit HelloWorld.Program
           extends [mscorlib]System.Object
    {
     .method private hidebysig static void  Main(string[] args) cil managed
      {
        .entrypoint
        // Code size       13 (0xd)
        .maxstack  8
        IL_0000:  nop
        IL_0001:  ldstr      "Hello World"
        IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
        IL_000b:  nop
        IL_000c:  ret
      } // end of method Program::Main
    
    
      .method public hidebysig specialname rtspecialname 
              instance void  .ctor() cil managed
      {
        .maxstack  8
        IL_0000:  ldarg.0
        IL_0001:  call       instance void [mscorlib]System.Object::.ctor()
        IL_0006:  ret
      } // end of method Program::.ctor
    
    } // end of class HelloWorld.Program

    Этот код хоть и состоит из опкодов, но опкоды эти напрямую на процессоре исполняться не будут. За дело берётся ваша среда исполнения, фреймворк. Когда вы запускаете этот код на Windows, .net возьмёт CIL и аккуратно сделает то, что называется JIT компиляцией. Just-In-Time. То есть, ваш код компилируется на лету. Вы запускаете программу на CIL, фреймворк быстренько её компилирует в инструкции вашего процессора и программа запускается. Плюсы такого подхода очевидны – если вы поменяли процессор на 64х битный, то ваша программа не только будет работать, но и будет правильно оптимизирована для конечного процессора.

    Ещё один плюс заключается в том, что не важно, на каком языке вы программируете. Если вы привыкли к BASIC вы на нём и продолжаете писать. Компилятор языка должен просто выдать вам опкоды CIL. И код для CIL из BASIC будет примерно такой же как и C#. .net поддерживает множество языков. Есть, например, F#, которым удобно писать функциональные программы. Есть Visual Basic, это выбор тех кто пришёл в программисты из Word и Excel. Конечно же есть С#, который писался как самый-самый язык для платформы.

    Акт номер 5: Яваскрипт

    Но тут можно пойти ещё дальше. Зачем компилировать код вообще? Почему бы просто не выполнять инструкции одна за другой? Так и сделали. Так появился javascript.

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

    Но, со временем, мы начали понимать, что javascript должен не только выполняться, но выполняться быстро. Посему программу, которая запускает ваш скрипт начали пилить. Всё это началось с войны браузеров в далёких 2000х. Тогда самым-самым браузером был Internet Explorer 6. Он победил всех. На сцену вышли Mozilla и Google. Началась война. Все боролись за скорость работы сайтов. Javascript начал компилироваться для ускорения производительности. Появились движки для javascript. V8, движок из Google Chrome, в итоге победил всех и вся. Осталось дождаться, когда Firefox переключится на него и вторую войну браузеров можно считать закрытой.

    После этого какой-то умелец взял V8 и запилил на нём Node.js. Зачем запускать javascript в браузере? Можно и без браузера. Научился писать на скриптах – пиши на них и серверную часть.

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

    Всё замечательно. Типа того. Количество кода и размер фреймворка начинают пугать. Чистый С компилируется практически без каких-либо издержек. Несколько функций стандартной библиотеки и всё. Java и .net тянут за собой порядка 200 мегабайт кода фреймворка, который надо установить на конечную машину. А уж накладные расходы javascript на память вселяют ужас.

    Конечно, компьютеры становятся мощнее и мощнее с каждым годом, и если в 1990х мы могли рассчитывать только на 8,000,000 операций в секунду, то в 2010 мы уже говорили о 3,000,000,000 в секунду. А сейчас мы хоть и опустились до 2,000,000,000, но зато у нас у всех под капотом по 8-16 ядер, которые выполняют эти операции одновременно, да ещё и видеоадаптер, который дополняет всю эту армию вычислительных машин. Так что, по большому счёту нам на скорость начхать. Какая разница если мы получим данные и покажем их на экране за 25 миллисекунд или за 250 миллисекунд? Пользователь всё равно не заметит разницы.

    Так и есть. Только это в ваших бизнес-приложениях он не заметит разницы. А вот если вы пишете игру, то разница между 25-ю и 250-ю миллисекундами очень даже заметна. Просто жесть как. Посему приходится выбирать.

    Акт номер 0x00000000: Память

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

    В древности ваша программа (в большинстве случаев) запускалась на процессоре как полновластный владелец всей системы. Вы могли обратиться к любому участку памяти, писать и читать из этого участка памяти и делать что вздумается. Этот подход был прост и ужасен. Вы могли в любой момент изменить память, которой пользовалась другая программа на компьютере. Решили сохранить в памяти огромную картинку с разрешением 320х240? Упс! Неправильно прописанный указатель переписывает содержание функций операционной системы, и вместо красивого изображения вы получаете полное зависание системы (если повезёт, если не повезёт, можно заодно и диск отформатировать).

    Вирусы в те стародавние времена писались только так. Почему бы не перехватить функцию в DOS, которая пишет данные на диск? Да проще простого! Перехватывай. Диски стирались, криптовались и чего только не делались. Делов-то.

    Со временем разработчики процессоров начали бороться с этим кошмаром путём усложнения модели работы с памятью. С 1985х в процессоры начали встраивать новый «защищённый режим» работы с памятью. Вместо того чтобы позволять каждой программе работать с памятью напрямую, программе предоставлялся интерфейс, для того чтобы запросить виртуальную память и работать с этой памятью. Память была виртуальной, потому что эта память не гарантировала выдачу вам блоков RAM с шестой по девятый. Вы просили систему дать вам полкило памяти, вы её получали. Когда ваша программа писала в эти полкило памяти, операционная система уже разбиралась что с этой памятью делать. В какой конкретно чип в RAM эти ваши данные положить. А если вы этими данными не пользовались, то система могла их и сбросить в Swap.

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

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

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

    Оффтоп: У Буратино было пять яблок. Он съел два. Сколько яблок осталось у Буратино? Думаете три? Фиг вам. 32764! Память кто будет инициализировать при работе с переменными?

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

    Что же, это просто, так ведь? Если попросили полкило памяти, то и отдайте полкило памяти. Делов-то. А что если вы попросили полкило памяти и отдаёте её обратно кусками по 10 килобайт? И вот, 2 куска в самом конце не отдали (забыли, поставили неправильный знак сравнения в цикле и последние два куска остались за вами). А потом вы попросили ещё полкило, то есть теперь ваша программа занимает 520 килобайт. И опять отдаёте её кусками и 2 куска не отдали? Теперь у нас 40 лишних килобайт. Результат:

    Такая штука называется утечкой памяти. И, в результате, вы сидите в 4 утра с красными глазами перед отладчиком и орёте благим матом «Ну где эта с_ка течёт?»

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

    А ещё можно обратиться к памяти по адресу 0x00000000 (нулевой адрес). Результат?

    А ещё можно…

    Короче вы поняли. Работа с памятью требует тщательного планирования и правильного управления. Любой программист на С и С++ обязательно проверит каждую функцию запроса памяти и убедится в том, что ничего нигде не течёт. Чем сложнее становится проект, тем сложнее эту память проверять. Появляются другие инструменты проверки. Например, знакомая каждому хабровчанину PVS-Studio. Статический анализатор, который может половить баги памяти в вашей программе.

    Программист, помни, работа с памятью – это очень тяжёлая ответственность. Ладно. Это всё решили в Яве и Дотнете.

    Акт номер 0xFFFFFFFF: Meet Garbage Collector

    Автоматический сборщик мусора! Зачем вообще программисту выделать память? Почему бы просто не сделать так – когда он создаёт переменную, пусть платформа сама возьмёт память с полочки. А когда переменная больше не нужна (программист выкинул все указатели на эту переменную) то отдельная программа которая запускается параллельно с вашей программой, называемая сборщик мусора, возьмёт и сотрёт эти данные из памяти.

    Всё просто прекрасно и хорошо. Но… Проблема в том, что сборщик мусора не всегда так уж и хорош и может создать ситуации, когда мусора становится слишком много. Когда ваше бизнес-приложение заточено на показ трёх значений курсов валют на экране, то сборщик мусора работает просто прекрасно. А вот если вы пытаетесь сделать ленту из видео файлов с «бесконечным скроллом» то сборщик мусора может запросить 8 гиг памяти у системы и конкретно всё затормозить.

    Идея сборки мусора перекочевала в javascript. В общем-то, это не очень плохая идея. Она просто накладная. У вас запускается не только ваша программа, но и программа, которая следит чтобы ваша программа нормально работала.

    Акт номер 6: LLVM

    Ладно, наступили двухтысячные. Ява с Дотнетом борются за лидерство. Яваскрипт завоёвывает серверную часть. А вот Си с плюсами начинает трясти. Проблема заключалась в том, что количество компиляторов перевалило за исчислимые пределы и теперь считается иррациональными числами. На сцене существуют тяжеловесы – Microsoft C++ Compiler и GNU C. Не то, чтобы эти компиляторы «воевали» друг с другом, войны между ними быть не может, ибо они рассчитанны для абсолютно разных систем.

    И тут на сцену выходит Apple, которая пытается сделать «всё для себя». Проблема вот в чём, самый популярный компилятор C для Unix-систем это gcc. Apple приходится использовать этот компилятор в своих продуктах, но лицензия GNU, которая идёт в добавку с этим компилятором накладывает жёсткие ограничения на политику яблоководов о том, чтобы сделать всё проприетарным. (Ежели вы берёте открытое программное обеспечение под лицензией GNU и дорабатываете его, вы обязаны оставить исходный код открытым.)

    И тут внезапно в 2003 году выскочка из университета Иллинойса показывает миру свою лабораторную работу под названием LLVM. Это название ничего не значит. Просто название. Что это за зверь такой?

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

    Вместе со всем этим выходит clang. Это компилятор для Си-образных языков, который собирает Си и С++ в LLVM код. То есть ваши программы на C можно запросто пропустить через этот компилятор. А на выходе у вас есть поддержка Windows, Linux, MacOS для кучи процессоров: х86, х86-64, PowerPC, ARM, AArch64 и тд. И самое главное – Apache 2.0 лицензия, в которой выпускать модифицированный код необязательно. То есть, Apple смогла взять этот LLVM и сделать его проприетарно-закрытым! Из инструментов разработки Apple пропадает GNU компилятор и его заменяет LLVM компилятор, который отлично компилируется в байткод для LLVM, который в своё время отлично собирается под Apple.

    Благодаря такому вниманию гиганта, проект получает поддержку и начинает развивать обороты. Хочешь написать свой язык? Пожалуйста! Пиши язык, который генерирует байт-код для LLVM, а мы его скомпилируем. Тебе не надо писать компилятор. Он уже есть. Просто пиши язык.

    Акт номер 7: И где же rust?

    И тут появляется rust. LLVM был просто средой компиляции. Работа с памятью как была проблемой программиста, так ею и осталась. Но ребята из Mozilla решили попробовать что-то новое. В 2010 они написали новый язык программирования, который использовал LLVM как компилятор.

    Язык был си-образным и очень низкоуровневым. В нём не было примочек в виде сборщика мусора или фреймворка на 200+ мегабайт.

    Но в язык была зашита принципиально новая система работы с памятью. Вместо того чтобы плодить БСОДы направо и налево, вам был дан borrow-checker. (Проверяльщик заимствования? Я не знаю, как правильно это перевести. Я его так и буду называть borrow-checker). Вшитый прямо в компилятор. Подход заключается в следующем: вы пишите код как будто у вас есть сборщик мусора. Вы просто объявляете строку и память для неё появляется автоматически. А после того как вы эту переменную используете, память освобождается сама по себе.

    Но это всё не происходит потому что у вас есть сборщик мусора. Компилятор сидит и запихивает куда нужно команды, которые запрашивают память у системы и освобождают эту память. Это не создаёт проблем с производительностью, ибо сходный код на С был бы так же быстр. Вам всё так же пришлось бы запрашивать память и освобождать её. Разница в том что вам не надо постоянно смотреть на БСОДы и Appcrash repots. Компилятор даст вам знать, что вот тут, в строке номер 10 у вас есть потенциальная ошибка памяти. Так что сидите и правьте.

    Следующая важная фича rust называется zero-cost abstractions. Это подход к разработке языка. Если вы даёте пользователю какую-то специальную функцию, то это функция не должна ничего стоить.

    Например, для того чтобы создать новый поток в С вы выполняете команду fork(). После того как вы выполнили эту команду процессор запускает две идентичные копии вашей программы. Теперь у вас есть два потока и программа выполняется дважды. Всё очень быстро. Но если вы пытаетесь создать новый поток в .net то всё будет не так уж просто. .net может создать несколько потоков при старте программы, а когда вы пытаетесь создать новый поток, .net может впихнуть туда много проверок на то, сколько потоков уже запущенно и можно ли запускать ещё несколько или нужно просто использовать уже созданные потоки. Короче, команда fork() в С создаст две копии вашей программы, в два потока. Команда System.Threading.Thread.Start() в С# может выполнить 50+ других команд создавая ваш поток.

    Когда вы создаёте новый поток в rust, то он не будет «тяжелее» fork(). Может быть в вашем коде вы увидите следующее:

    HttpServer::new(move || { 
    	App::new() 
      .wrap(middleware::Logger::default())
      .service(index) 
    });

    Выглядит это всё круто, но под капотом этого странного способа создавать замыкания в rust (||) вы найдёте fork(). Rust сам по себе не будет писать менеджер потоков за вас. Посему, писать всё придётся самому. Если тебе нужен менеджер соединений с базой данных, пожалуйста, вот тебе чистый лист, пиши. (Либо ищи и качай его с crates.io)

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

    Да. Всё прекрасно. Но rust это не простой язык для изучения. Вам надо очень хорошо разбираться в архитектуре компьютеров и понимать как на самом деле работает память. Вам надо хорошо знать системное программирование и иметь хорошее представление о том как работает heap и stack в операционной системе. Rust не прощает ошибок. Но, в отличие от C и C++, rust не прощает ошибок с памятью на этапе компиляции.

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

    Акт номер 0xFE: Заключение

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

    На самом деле – это те вопросы, на которые вам надо ответить. Если вы пишете скрипт, который вы выкинете через 2 дня, пишите его на чём попало. Главное, чтобы не в продакшин.

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

    Играетесь с новым адуриноподобным устройством на ARM? Попробуйте rust! Будет весело.

    Если у вас есть WEB приложение, которое должно работать на одном сервере и обслуживать 200 человек в вашей организации, то вам будет достаточно node.js. На нём писать проще. Когда компания вырастает до 2000 человек, то надо уже серьёзно подумывать про переход на шарпы или яву. Но когда аудитория сайта переваливает за 200000 человек, то мы можем говорить о переходе на rust.

    Или вот, например, у вас есть docker контейнер, в котором у вас вертится микросервис написанный на Java. Этот «микросервис» занимает 200+ мегабайт места на жёстком диске. С использованием C или Rust вы могли бы это сократить до 20 мегабайт. Обновление и запихивание всего этого дела на серверы может прилично сократить трафик.

    На rust написаны куски кода для Discord, Microsoft Azure IoT Engine, Open DNS, Firefox, Linux Kernel, и тд.

    Для кого rust? Для суровых бородатых системных программистов, которые хотят скорости и готовы часами проектировать своё программное обеспечение с учётом потребления памяти и производительности. Написание игр или компонентов рендеринга, драйверов устройств и так далее – это удел rust.

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

    Написание WEB приложения на rust это не обязательно полезная деятельность. Если вы действительно хорошо разбираетесь в системном программировании, и можете писать на С++ с закрытыми глазами, то вам стоит писать WEB на rust. Иначе, вы будете часами сидеть перед функцией, которая принимает параметры возвращаемые из формы, и горько плакать, потому что перед вами будет стоять непосильная задача распарсить эти параметры в JSON.

    Акт номер 0xFF: Учимся

    Если вы решили учить rust для WEB программирования, но ещё никогда им не пользовались, то для начала я рекомендую выучить rust, читая официальное руководство. Я очень сильно не советую пытаться начать пилить серверную часть на rust если вы никогда ничего на этом языке не писали.

    Для начала, напишите хотя-бы парсер текста. Простой grep, например. Потом разберитесь, как эту программу распарралелить. Когда вы научитесь пользоваться областями видимости и без проблем будете создавать замыкания и новые потоки, можно будет переключаться именно на WEB программирование на rust.

    После того как язык перестанет быть для вас полной кашей, можете обратиться к “Fullstack Rust” by Andrew Weiss. Я никакого отношения к этой книге не имею, за исключением того, что я её прочитал, и она в деталях объясняет, как правильно писать WEB сервер на rust, как пользоваться wasm и так далее. Не пытайтесь читать эту книгу, если вы ничего про rust не знаете.

    А если не хотите обзаводиться этой книгой, что же, не проблема! Мы лёгких путей не ищем!

    1. Учим rust по официальному руководству.

    2. После этого идём в http://actix.rs и учим его. Это один из самых передовых WEB фреймворков для rust.

    3. Можно выучить rocket – это самый новый WEB фреймворк для rust. Здесь синтаксис проще и работа быстрее. Единственное что мне в rocket не понравилось – это то что он не компилируется на стандартном компиляторе rust и заставляет вас установить nightly билд. Это не очень хорошо для продакшена.

    4. После актикса можно учить http://diesel.rs. Это фреймворк для работы с базами данных.

    5. Попутно выучите serde. Систему сериализации и десереализации данных в rust.

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

    6. Ну а если вы хотите окунуться в новый мир невероятного wasm то начать надо будет с: https://github.com/rustwasm/wasm-bindgen

    7. После надо заняться https://rustwasm.github.io/docs/wasm-pack/introduction.html

    В попытках выучить всё это вы наткнётесь на кучу различных пакетов, про которые стоит знать. Натыкайтесь. Будет весело.

    Читать дофига, учиться и того больше. Ставим компилятор rust и начинаем эксперименты.

    Успехов!

    Only registered users can participate in poll. Log in, please.

    Что писать дальше?

    • 48.3%Расскажи как на самом деле работает компилятор rust. Покажи его кишки.113
    • 37.2%Хватит кота тянуть за хвост. Айда писать web сервер на rust!87
    • 36.3%Хочу крови и извращений. Почему бы не запилить что-нибудь на WebAssembly?85
    • 29.1%Мы люди простые, давай разберёмся в азах. Покажи всякие хеллоуворлды.68

    Similar posts

    Ads
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More

    Comments 88

      +2
      Микросервисы на яве едят под 1-1.5гб рам (спринг бут, mvc, все дела). Есть ли смысл (в плане уменьшения потребления рам) смотреть в сторону переписывания нескольких таких микросервисов на раст?
        +1

        Ну если у вас крутится под сотенку контейнеров для обработки медиа, то разница между 200 метрами и 1,5 гигами будет заметной.

          +4
          Есть смысл для начала выкинуть этот самый бут/mvc и переписать на той же яве.
            0

            Java в работе с памятью не так плоха, есть GraalVm и те же микрофреймворки типа quarkus и иже с ними
            Потребление памяти в них приятно удивляет

              +1

              Есть. Если есть интерес к Rust и хочется его опробовать — то так и нужно сделать. В противном случае имейте ввиду, что придется потратить время на освоение программирования на Rust.

            • UFO just landed and posted this here
                +2

                Ну, всё, пропали выходные. Зарываемся в compiler manual.

                +3

                Кажется, единственное (кроме borrow checker’а, конечно), что rust предлагает принципиально нового в сравнении с другими компилируемыми языками — это first class citizen AST.


                Вот именно про AST было бы весьма интересно, потому что тогда можно было бы говорить о действительно интересной парадигме, позволяющей вывести интероперабельность на новый уровень — а не очередном невнятном синтаксисе и «avoid success at any cost».

                  +6
                  Справедливости ради стоит отметить, что в LISP-подобных это есть уже точно не 1-е и не 2-е десятилетие.
                    +5

                    Чем оно более first class, чем TH?

                      0

                      Если TH — это Type Hints, то я не понял вопрос. «Чем ананас более фрукт, чем яблоко?» — да ничем, просто TH как бы не являются meta-language facility, а AST является.


                      Вот я беру AST эликсира в узких местах — и почти диффеоморфно конвертирую его в Julia’s AST. И арифметика, написанная на эликсире, внезапно работает быстро. Или я могу нашему RnD реализовать алгоритм, не засоряя голову синтаксисом Julia. Как тут помогут TH?

                        +4

                        В данном случае имелся в виду Template Haskell. Могли бы и сами догадаться зная написавшего…

                          0

                          Во-первых, я не настолько тупорылый дегенерат, чтобы принимать во внимание личность человека в дискуссии в интернете. Во-вторых, я написавшего впервые вижу. И, наконец, я написавшего немного уважаю, поэтому вариант Template Haskell отмел в контексте вопроса сразу, как GHC extension к Haskell 98. Я не думал, признаться, что у кого-то достанет смелости назвать это «first class citizen» в языке.

                            +3
                            Я не думал, признаться, что у кого-то достанет смелости назвать это «first class citizen» в языке.

                            Мы же это уже обсуждали — надо прекратить пинать дохлую лошадь и признать, что попытки иметь какой-то стандарт хаскеля после Haskell Report 2010 провалились (ибо никому оказались не нужны), а затем принять, что хаскель — это вот ровно то, что реализовано в ghc.

                              0
                              GHC extension к Haskell 98

                              Ключевое слово 98, даже не extension. Это костыль, вкряченный сбоку. Я же не говорю, что оно не работает. Как-то работает, наверное. Даже в руби спустя лет двадцать добавили какое-то подобие AST, это всегда возможно, просто в силу структуры языков программирования.


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

                              +1

                              Ну «ДедушкаФуд», главный хаскелист всея Хабра. И знаток прочих мозголомных языков, как нистранно использующихся на практике. У меня мозг взрывается, когда смотрю на его APL-подобные листинги. ;-)

                      +3
                      Призывы в конце статьи как-то очень сильно напоминают «первая доза rust бесплатно» :-)
                        +5
                        Если вы действительно хорошо разбираетесь в системном программирвоании, и можете писать на С++ с закрытыми глазами, то вам стоит писать WEB на rust. Иначе, вы будете часами сидеть перед функцией, которая принимает параметры возвращаемые из формы, и горько плакать, потому что перед вами будет стоять непосильная задача распарсить эти параметры в JSON.

                        Да нет же, есть куча удобного в C++ для этого, тот же nlohmann/json к примеру.
                        От чего такие обвинения? От незнания?
                          +3

                          А можете показать пример, как это всё выглядит? А то на Rust я могу сделать вот так:


                          use serde::Deserialize;
                          
                          #[derive(Deserialize)] // <-- единственное, что нужно добавить к определению
                          struct User {
                              name: String,
                              age: u16,
                              attributes: Vec<String>
                          }
                          
                          fn main() {
                              let input = r#"{
                                  "name": "John Doe",
                                  "age": 32,
                                  "attributes": ["smoker", "programmer"]
                              }"#;
                              let user: User = serde_json::from_str(input).expect("parse error");
                              assert_eq!(user.name, "John Doe");
                              assert_eq!(user.age, 32);
                              assert_eq!(user.attributes, ["smoker", "programmer"]);
                          }

                          И всё прекрасно работает

                            0

                            Если говорить конкретно про nlohmann/json, то там есть ряд примеров прямо в README:


                            https://github.com/nlohmann/json

                              +3

                              Не увидел аналога deriving #[derive].

                                +1
                                Там есть нечто вроде этого — макросы NLOHMANN_DEFINE_TYPE_XXX. А вот что касается возможности парсить json, так сказать, «js-style» (в динамические структуры) — в serde я этого не увидел, по крайней мере, сходу беглым взглядом.
                                  +3

                                  Потому что эта возможность есть не в serde, а в serde_json: https://docs.serde.rs/serde_json/

                                    +3

                                    Все эти макросы плохи тем, что если у вас одна библиотека предлагает NLOHMANN_DEFINE_TYPE_XXX для поддержки жсона, другая — BOHMANN_DEFINE_TYPE_YYY для поддержки бсона, а третья — BERMANN_DEFINE_TYPE_ZZZ для поддержки BER, то совершенно непонятно, как их совмещать.

                                      +2
                                      Там есть нечто вроде этого — макросы NLOHMANN_DEFINE_TYPE_XXX

                                      Благодарю. А теперь покажите, пожалуйста, аналоги rename, default и flatten, а что-то руками писать свой десериализатор страшно не хочется.

                                        0

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

                                  –1

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

                                +1
                                А можно на Расте писать GUI-шные программы? Есть ли какая-то IDE и Form Designer? Чтобы по степени удобства было сравнимо например с Lazarus?
                                  +2

                                  Можно. Я доберусь — напишу.

                                    +3

                                    Можно, но все еще в довольно зачаточном состоянии. Есть несколько врапперов, над Qt, GTK итп. Есть несколько сырых rust-centric фреймворков. Пока самый "взрослый" способ делать GUI, из тех, что я нашел — это веб, как раз yew.


                                    https://www.areweguiyet.com/

                                    +3

                                    Часть про Web освещена как то слабо, ждешь чего-то большего личного опыта, кейсов
                                    В каких случаях и по каким показателям, кроме performance, дает ощутимый прирост в разработке, стоимость поддержки и т.д.

                                      +3

                                      До главки "Javascript" было хорошо. А дальше, как говорится, панеслася…
                                      1) JS в 2021 году на 99.9% (0.01% приходится на исполнение JS в странных браузерах без JIT-компиляции) всё так же проходит JIT-компиляцию. Никто вам его не "просто берет и начинает выполнять".
                                      2) Критиковать размер зависимостей в JS рядом с разговорами o wasm — очень смешно. Потому что если в ноде у вас ситуация "очень большой объем зависимостей на JS и немного меньше, если нативно" — более-менее распространена, то вот на фронте гораздо более распространена обратная ситуация — попытки сделать фронт на wasm ведут к тому, что итоговый размер пакета куда больше объема JS (потому что в пакет wasm вы вынуждены складывать то, что уже бесплатно есть в браузере для JS).

                                        +1

                                        JIT — не панацея. Есть исследования, к сожалению, ссылки не сохранял, что JIT не может работать настолько же эффективно, как нативный код в принципе.


                                        Сборка мусора — тоже добавляет сложностей и неэффективностей, которые никак не обойти.

                                          +1

                                          Да ну конечно не может. С этим никто и не спорит.
                                          Но дело в том, что "работать максимально эффективно" нужно далеко не везде.

                                          0
                                          в пакет wasm вы вынуждены складывать то, что уже бесплатно есть в браузере для JS

                                          В каком смысле? wasm (как и сам JS) имеет доступ ко всему что в браузере есть для JS — туда не нужно что-то дополнительно складывать, разве что очень короткий прокси сделать. Если кто-то это делает — то это от незнания, а не по необходимости.

                                            0

                                            В прямом. Если вы из wasm будете ходить в браузерный мир, то от единственного преимущества wasm (быстродействия) не останется совсем ничего. Всё будет работать со скоростью того самого JS, который вы будете постоянно дергать.

                                              +1

                                              Преимущество wasm проявляет себя исключительно в вычислительно-интенсивных операциях, а всё что касается UI, рендеринга и прочего — это всё равно делается через песочницу браузера, а она абсолютно идентична что для wasm что для JS, ускорить это невозможно в принципе, разве что реализовать свой специализированный браузер на WebGL.


                                              То есть чтобы вы не "складывали" в wasm — если это касается DOM, картинок, стилей etc — это будет ровно так же быстро как и в JS, но никак не быстрее.

                                                +1
                                                Преимущество wasm проявляет себя исключительно в вычислительно-интенсивных операциях

                                                … и вот поэтому если вам для "вычислительно-интенсивных операций" понадобится ГСЧ, вы как миленький положите код этого ГСЧ в пакет, потому что обращения к браузерному Math.random() все ваши преимущества размажут.
                                                И ГСЧ — это, безусловно, мелочь. Но обработка строк — уже не такая мелочь. Сложные структуры данных — аналогично. И так далее и так далее.

                                                  +1

                                                  Все современные популярные браузеры содержат высокэффективные реализации примитивов и криптографии, в т.ч. штук типа Math.random() и обработки строк (примерно также это сделано в NodeJS) — таким образом нивелируя различия по скорости для wasm и JS.


                                                  С учётом этого, тащить в wasm имеет смысл только то к чему нельзя обратится из JS — всё остальное с вероятностью 99% уже и так максимально эффективно, а сами браузеры, насколько мне известно, не содержат в себе библиотечного кода на JS.

                                                    +1

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


                                                    Либо вы кладете в пакет wasm весь нужный вам "для вычислительно-интенсивных операций" код, либо вы обращаетесь к браузерной реализации по маршруту wasm -> обертка js -> экосистема браузера -> обертка js -> wasm. И тогда это всё работает со скоростью js.

                                                      +1
                                                      Либо вы кладете в пакет wasm весь нужный вам "для вычислительно-интенсивных операций" код

                                                      Если он специфичный для моего приложения — то в браузере его не будет "бесплатно" и для JS тоже.


                                                      Выше вы написали:


                                                      в пакет wasm вы вынуждены складывать то, что уже бесплатно есть в браузере для JS

                                                      Повторю — если это уже есть и бесплатно для JS (т.е. без подкачки откуда-то извне) — значит оно бесплатно для wasm тоже, ибо последний имеет прямой доступ к JS API браузера, оба (wasm и JS) выполняются в одной и той же VM.


                                                      Т.е. грубо говоря вызов Math.random() из JS и wasm занимает ровно одно и то же время (в случае wasm таки может быть даже быстрее потому что нет оверхеда движка JS), а ваша собственная реализация такого же random() на wasm никогда не будет быстрее браузерной по определению (разве что браузерные девелоперы совсем уж накосячили) — по той простой причине что браузерная почти наверняка написана на C/C++ и хорошо оптимизирована, а wasm (будучи банальным байткодом, похожим по сути на JVM) просто проходит через JIT и не достигнет такой же производительности.


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

                                                        0
                                                        Либо вы кладете в пакет wasm весь нужный вам «для вычислительно-интенсивных операций» код, либо вы обращаетесь к браузерной реализации по маршруту wasm -> обертка js -> экосистема браузера -> обертка js -> wasm. И тогда это всё работает со скоростью js.
                                                        И даже медленней. В отличии от «js only», у вас появятся еще накладные расходы от взаимодействия «wasm <--> js».
                                            +8

                                            Что-то про fork и потоки намешано не то. fork() — это про процессы, а не потоки. Inter-processing communications всё же слегка сложнее коммуникации между потоками.


                                            Пример с zero cost стоило сделать с другой конструкцией всё же.

                                              +3

                                              Я вообще не понял про zero cost. Я всегда думал, что это про то, что если в языке есть какая то фича, но ты ей НЕ пользуешься, то наличие этой фичи — никак не влияет на твою программу.
                                              Ну например в C++ виртуальные методы — zero cost, не используешь их, не платишь за косвенность вызова. А исключения — не zero. Компилятор должен сильно поприседать, чтоб понять, что тут исключений нет и try finally делать ненужно


                                              А тут явно вызвали функцию fork(), понятно, что это уже не zero cost и пофиг что она конкретно делает.

                                                +1

                                                Исключения давно zero-cost для случая невыкидывания экзепшона.


                                                Вы, конечно, можете сказать, что невозможность доказать невыкидывание экзепшона в какой-то точке мешает оптимизации, но тогда вы должны будете признать, что даже ссылки и указатели в C++ не являются zero-cost.

                                                  +1

                                                  Под x86 zero cost?


                                                  тогда вы должны будете признать, что даже ссылки и указатели в C++ не являются zero-cost.
                                                  Хм, а как ссылки и указатели мешают оптимизации если я их не использую?

                                                    +1

                                                    Фиг знает, под x86 (не _64) я очень давно код не собирал. И не знаю никого, кто собирал бы под x86 требовательный к производительности код.


                                                    Хм, а как ссылки и указатели мешают оптимизации если я их не использую?

                                                    Компилятор далеко не всегда может доказать, что вот эти два указателя не алиасятся (даже если вы их не алиасите сами), или что вот эта переменная не изменится при записи по вот тому указателю.

                                              +3
                                              Спасибо. Как беглый исторический экскурс прочитал с большим удовольствием.
                                                +4
                                                Классные статьи про классный язык, начал читать книгу по вашему пути… Но найдите таки правильный синий экран. Который про ошибку обращения к памяти, а не про обращение к выгружаемой памяти в драйвере не с того уровня ;)
                                                  +1
                                                  я считаю подход начинать с идеи того, что хотим сделать, мотивирует больше, чем детали работы железа. процессор, память и т.д.
                                                    +10
                                                    Оффтоп: У Буратино было пять яблок. Он съел два. Сколько яблок осталось у Буратино? Думаете три? Фиг вам. 32764! Память кто будет инициализировать при работе с переменными?

                                                    «У Буратино было пять яблок» это и есть инициализация. Чтобы создать правильную ошибку не инициализированный переменной нужно например «Буратино дали пять яблок...»
                                                      +1

                                                      И если там беззнаковый WORD — то ещё и 65534.

                                                        +2

                                                        Таки не факт, что полученная от ОС неинициализированная память была забита 0xFF, может там были DE AD и тогда яблок осталось 57003, в случае беззнакового :-)

                                                          +1

                                                          Да, тоже верно. В общем "фиг его знает, сколько тех яблок".

                                                        0

                                                        Ой и правда. В оригинале было так: Буратино дали пять яблок. Он съел 2. Сколько яблок у Буратино?

                                                        +4

                                                        Почему я использую Rust в вебе — и в рабочих, и в личных проектах:


                                                        1. Хорошая производительность "из коробки" и хороший потенциал для тюнинга оной в дальнейшем.
                                                        2. Универсальность языка и хорошая поддержка модульности — можно активно переиспользовать код в широком спектре разных проектов.
                                                        3. Поддержка WebAssembly, возможность писать клиент и сервер на одном языке с использованием общего кода.
                                                        4. Развитая система типов упрощает доработку и поддержку.
                                                        5. Относительно просто писать многопоточный и асинхронный код.
                                                        6. Удобное управление ресурсами (встроенное RAII как для памяти, так и для чего угодно еще).
                                                        7. Легковесность, отличный тулинг.

                                                        Но я бы не советовал его брать тем, кто:


                                                        • Не готов осваивать новую концепцию программирования (владение и заимствование).
                                                        • Не готов терпеть сыроватую экосистему, в которую время от времени нужно котрибьютить.
                                                        • Не собирается развивать свой проект, а хочет быстро написать прототип и выбросить.
                                                        • Предпочитает программирование в стиле REPL.
                                                          +3

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

                                                            +1
                                                            Почему я использую Rust в вебе — и в рабочих, и в личных проектах:


                                                            Можно чуть больше инфы что за проекты? Без закрытой бизнес инфы, само собой. Хотя бы примерную референс архитектуру (риалтайм аналитика, iot etc), сколько отдельных сервисов в системе и где там используется Rust.
                                                              +5

                                                              Один проект — это торговая площадка, криптовалютная биржа. Ее ядро написано на Rust (actix, gRPC, RocksDB) + некоторые дополнительные сервисы и инструменты.


                                                              Другой проект — платформа для запуска децентрализованных приложений в изолированном окружении. Клиент, сервер и сами приложения пишу на Rust (actix_web, yew, wasmer, libp2p).

                                                                0

                                                                Замечательно! Не хотите написать статью? А то народ требует хлеба и зрелищ а я по колено в коде компилятора, пишу следующую статью. И до этого вэба не доберусь. А как видно из опросов, веб на втором месте.

                                                                  0

                                                                  А изолированное окружение на основе чего сделано? cgroups+namespaces, systemd-nspawn или что-то ещё?

                                                              –1
                                                              Спасибо, хорошее описание… Вебку на Rust — это жуть, лучше другие вещи. Вот например блокчейн, код одного из которых видел однажды видел на Rust-е, и это было единственное, что вообще видел на нем…
                                                                +6

                                                                Почему жуть? Вот перелопатить половину JavaScript-приложения только для того, чтобы понять все возможные значения, которые могут посылаться в данную функцию — вот это жуть. Rust от такого спасает.

                                                                  0
                                                                  Зачем JS? Питон вполне устраивает для веба.
                                                                    +2

                                                                    В питоне вышеозначенная проблема точно так же присутствует

                                                                +2
                                                                  –1
                                                                  Не знаю что я делаю не так, но, я сравнил 2 примерно одинаковые утилиты на golang и rust, которые разбирают большой xml файл (размером более 22 гигабайт), — просто вытаскивают из него нужные элементы, используя SAX парсинг (такой DOM не влезет в память). Так вот утилита на golang, которая делает сильно больше (разбор + создание неких объектов + вставка этих объектов в mongodb), работает в 2 РАЗА БЫСТРЕЕ (6 минут против 14), при этом код на rust просто считает количество открытых и закрытых тегов, ничего не вставляет в базу и не создает.

                                                                  В качестве SAX парсера на rust я использовал xml-rs = «0.8» (https://docs.rs/crate/xml-rs/0.8.3)
                                                                    +4

                                                                    Хотелось бы посмотреть на код в обеих утилитах, а заодно узнать, насколько быстрее будет с quick-xml

                                                                      +2

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

                                                                        0
                                                                        c quick-xml все сильно быстрее.

                                                                        В общем вывод — все зависит от используемого crait
                                                                        0
                                                                        Да пожалуйста:

                                                                        вот для rust gist.github.com/aegoroff/8fd8161c9aa56b13fe1b151f16815871
                                                                        вот для Golang gist.github.com/aegoroff/8bf37611a89053ebb0b8eee35fbc2f5a

                                                                        с последним попробую, спасибо
                                                                          +5
                                                                          Замена xml-rs на quick-xml уменьшила время разбора с 880 секунд до 25 т.е. в 35 раз!

                                                                          что-то явно не так с xml-rs

                                                                          При работе с xml-rs у меня io приложения порядка 30 мб/сек, с quick-xml доходит до 1 гб/сек
                                                                            +1
                                                                            полезная инфа, спасибо. Надо держать ухо востро с крэйтами. Знать бы теперь как их навскидку оценивать
                                                                              +1
                                                                              Навскидку — по количеству загрузок, но нельзя забывать, что это могут быть «миллионы мух».
                                                                              Если чуть детальнее — смотреть список зависимых крейтов, если там есть другие относительно популярные — тогда чуть понадёжнее.
                                                                                +1
                                                                                Ну вот как раз я xml-rs и выбирал по количеству загрузок — у него их больше всего, среди тех, что работают с XML

                                                                                crates.io/search?q=xml&sort=downloads
                                                                                  +3
                                                                                  При поиске на очень конкретную тему рекомендую в первую очередь смотреть на keyword:
                                                                                  crates.io/keywords/xml
                                                                                  Тогда у вас
                                                                                  quick-xml v0.20.0
                                                                                  High performance xml reader and writer
                                                                                  был бы сразу вторым.

                                                                                  Но соглашусь, к сожалению самое популярное не всегда самое подходящее.
                                                                                +2
                                                                                на самом деле 25 секунд оказалось только в первый раз — все остальные за 15 секунд проходят :) так, как кэширование OS начинает работать, и скорость IO возрастает до 1.5 Гб/сек, разумеется такие цифры на NVMe SSD получаются
                                                                            +1

                                                                            Надеюсь растовую версию вы собираете с профилем release?

                                                                              +1
                                                                              конечно
                                                                            +1
                                                                            а как же Tokio.rs он не про веб?
                                                                              +1
                                                                              Tokio — это «всего лишь» асинхронная база для любого проекта, не обязательно веб.
                                                                              –1
                                                                              Проблема с компилируемыми языками на сервере называется «а как это собирать?». Linux — это ж не благословенная винда, где вы можете собрать всё на вашей рабочей машине, потом просто скопировать готовый exe'шник на сервер и всё будет работать. Тут так нельзя. И что делать? Тянуть на сервер компилятор и весь набор инструментов/библиотек, а потом прямо на сервере вести сборку? Как-то оно не очень выглядит.
                                                                                +1
                                                                                Для этого придумали RPM/DEB пакеты, или даже Docker — упаковал на своей машине разработчика, перенёс любым способом и развернул одной командой.
                                                                                  0

                                                                                  Пакеты с бинарниками обычно прибиты гвоздями к конкретной (мажорной) версии системы где собирались, а докер точно такая же зависимость как компилятор с библиотеками — он не стоит по умолчанию на всех без исключения системах, не везде может быть установлен (по административным или техническим причинам) и (в зависимости от приложения) может оказаться не бесплатен в плане производительности (все эти слои поверх fs/net чего-то стоят) или удобства конфигурации (ему нужны свои очень специальные правила для файрволла), да и вообще докер часто это просто способ масштабирования "кое-как удалось собрать" и "у меня всё работает" на массы чайников которым не удалось собрать или у кого не получается заставить работать.


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

                                                                                    +1
                                                                                    Хорошо, а что вам мешает завести билд-машину с той же версией ОС, что и на целевой(-ых) машинах и опять-таки собирать в пакеты?
                                                                                      +1

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


                                                                                      К сожалению, порог входа для дистрибуции бинарников очень высок в случае Линукса, а само пространство очень фрагментировано, как по софту так и по политикам.

                                                                                        +1

                                                                                        А причём тут опенсорс и всевозможные вариации, когда обсуждалась проблема применимости компилируемых языков на сервере? Уж для своих-то серверов собрать правильную билд-машину вполне возможно.

                                                                                  +1

                                                                                  ну сейчас руками не принято собирать, для этого используют CI/CD и по возможности докер (который содержит всё необходимое)

                                                                                Only users with full accounts can post comments. Log in, please.