Язык программирования Rust, начатый как хобби-проект, а впоследствии поддерживаемый корпорацией Mozilla, позволяет обычным программистам писать одновременно и безопасные и быстрые системы: от калькуляторов до высоконагруженных серверов.


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


  1. Rust — сложный язык программирования
  2. Rust — ещё один "убийца C/C++"
  3. Unsafe губит все гарантии, предоставляемые Rust
  4. Rust никогда не обгонит C/C++ по скорости

1. Rust — сложный язык программирования


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




Данный стереотип восходит своими корнями к концепции времён жизни ссылок (lifetimes), позволяющей на уровне семантики языка описывать гарантии действительности используемых ссылок. Синтаксис лайфтаймов выглядит сперва странным:


struct R<'a>(&'a i32);
unsafe fn extend_lifetime<'b>(r: R<'b>) -> R<'static> {
    std::mem::transmute::<R<'b>, R<'static>>(r)
}

unsafe fn shorten_invariant_lifetime<'b, 'c>(r: &'b mut R<'static>)
                                             -> &'b mut R<'c> {
    std::mem::transmute::<&'b mut R<'static>, &'b mut R<'c>>(r)
}

Но на деле синтаксис объявления лайфтайма довольно прост — это всего лишь идентификатор, перед которым следует апостроф. Лайфтайм 'static означает, что ссылка является действительной на протяжении всего времени исполнения программы.


Что такое "действительность ссылки"? Если ссылка действительна, то она поддаётся разыменованию без паники, ошибки сегментации и прочих прелестей. Например, в функции main() указатель something становится недействительным, т.к. все автоматические переменные функции produce_something() очищаются после её вызова:


int *produce_something(void) {
    int something = 483;
    return &something;
}

int main(void) {
    int *something = produce_something();
    int dereferenced = *something; // Segmentation fault (core dumped)
}

Семантически лайфтайм может быть задан посредством других лайфтаймов. Например, эта функция требует того, чтобы ссылка foo не стала недействительной до недействительности ссылки bar:


fn sum<'a, 'b: 'a>(foo: &'b i32, bar: &'a i32) -> i32 {
    return foo + bar;
}

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




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


Рассмотрим на примере. В приведённом ниже коде переменная x владеет экземпляром структуры Foo, а переменная y заимствует значение, которым владеет переменная x:


struct Foo {
    data: Vec<u8>,
}

fn main() {
    let x = Foo { data: Vec::new() }; // Владение (owning)
    let y = &x; // Заимствование (borrowing)
}

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


  • Значение может быть заимствовано иммутабельными переменными множество раз и при этом не заимствовано мутабельной;
  • Значение может быть заимствовано мутабельной переменной лишь один раз и при этом не быть заимствованным иммутабельными.

2. Rust — ещё один "убийца C/C++"


Ключевая фраза — "ещё один". На данный момент Rust — единственный язык программирования, обладающий одновременно активным сообществом и характеристиками, позволяющими ему решать задачи, решаемые языками C/C++. Синтаксис и семантика позволяют с лёгкостью изъясняться на разных уровнях абстракции — от инструкций SIMD до управления веб-серверами.


Данный стереотип возник вследствие языков Vala, Zig, Golang и подобных. Как я сказал выше, у этих языков либо слишком маленькое сообщество, либо они теоретически и практически не смогут работать на всех системах, на которых способны работать C/C++. У Vala и Zig маленькое сообщество, а Golang берёт курс на вытеснение интерпретируемых языков и не может работать на системах с критической нехваткой ресурсов, т.к. поставляется с дополнительной средой выполнения (например, сборщик мусора).


Очевидно, что языки C/C++ будут жить ещё очень много лет из-за накопленного за десятилетия кода и программистов, пишущих на них, но Rust имеет все шансы потеснить их, как это когда-то сделала Java.


3. Unsafe губит все гарантии, предоставляемые Rust


Unsafe — это конструкция языка, позволяющая совершать операции, способные привести к неопределённому поведению (UB). В действительности, unsafe позволяет делать лишь четыре операции, запрещённые в "безопасном" Rust:


  • Вызов небезопасной функции;
  • Реализация небезопасного трейта;
  • Разыменование глобальной статической мутабельной переменной;
  • Разыменование сырого указателя.

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


fn safe_display() {
    unsafe {
        let x = 385;
        let x_ref: *const i32 = &x;
        println!("{}", *x_ref);
    }
}

Функция safe_display() полностью безопасна, т.к. правильность потенциально небезопасного блока формально доказуема. Пользователь может использовать эту функцию без боязни UB.


Данный стереотип можно встретить в несколько иной трактовке: "Rust станет популярным лишь тогда, когда его сделают полностью небезопасным". И снова неверно, т.к. не все гарантии, предоставляемые Rust, могут работать в полностью небезопасном коде. Концепция Rust теряется при отсутствии гарантий безопасности.


4. Rust никогда не обгонит C/C++ по скорости


Утверждение безосновательное. В теории, программа, написанная на языке Rust, может быть оптимизирована столь же хорошо, как и аналогичная программа на C/C++. В некоторых синтетических тестах производительности Rust даже обгоняет GCC C:





Что касается тестов производительности на реальных задачах, то можно отметить замеры производительности RapidJSON и serde_json. serde_json парсит DOM медленнее, чем это делает RapidJSON, но при сериализации/десериализации структур serde_json обогнал RapidJSON (DOM) как на GCC, так и на CLANG:



Также можно отметить библиотеку Rustls, обогнавшую знаменитую OpenSSL практически во всех тестах (на 10% быстрее при установке соединения на сервере и на 20%-40% быстрее на клиенте, на 10%-20% быстрее при восстановлении соединения на сервере и на 30%-70% быстрее на клиенте).




По сути, когда мы сравниваем производительность Rust, скомпилированного стандартным компилятором rustc, с производительностью C/C++ кода, скомпилированного посредством CLANG, то мы сравниваем качество сгенерированного LLVM IR. В один случаях он может быть более подвержен оптимизациям, в других — менее.


На данном этапе своего развития компилятор rustc ещё не научился генерировать минимальный набор инструкций в сравнении с GCC/CLANG, что, теоретически, может замедлить выполнение программы на пару процентов. Рассмотрим два аналогичных примера кода на Rust и на C CLANG:


Код:
[https://godbolt.org/z/7b46MD]


pub fn compute(input: &mut [i64; 8]) {
    for i in 0..input.len() {
        input[i] = (input[i] + 3254) * 3;
    }
}

Выхлоп
<T as core::convert::From<T>>::from:
        mov     rax, rdi
        ret

<T as core::convert::Into<U>>::into:
        mov     rax, rdi
        ret

<T as core::convert::TryFrom<U>>::try_from:
        mov     rax, rdi
        ret

<I as core::iter::traits::collect::IntoIterator>::into_iter:
        mov     rdx, rsi
        mov     rax, rdi
        ret

example::compute:
        xor     eax, eax
        jmp     .LBB4_1
.LBB4_3:
        mov     rcx, qword ptr [rdi + 8*rax]
        lea     rcx, [rcx + 2*rcx]
        add     rcx, 9762
        mov     qword ptr [rdi + 8*rax], rcx
        inc     rax
.LBB4_1:
        cmp     rax, 8
        jne     .LBB4_3
        ret

Код:
[https://godbolt.org/z/YOey3P]


#include <stdint.h>

void compute(int64_t *input) {
    for (int i = 0; i < 8; i++) {
        input[i] = (input[i] + 3254) * 3;
    }
}

Выхлоп
compute:                                # @compute
        xor     eax, eax
.LBB0_1:                                # =>This Inner Loop Header: Depth=1
        cmp     rax, 8
        je      .LBB0_2
        mov     rcx, qword ptr [rdi + 8*rax]
        lea     rcx, [rcx + 2*rcx]
        add     rcx, 9762
        mov     qword ptr [rdi + 8*rax], rcx
        inc     rax
        jmp     .LBB0_1
.LBB0_2:
        ret

rustc сгенерировал больше инструкций, чем CLANG, хотя под капотом одна технология — LLVM. Это недоработка компилятора rustc, но никак не ограничение самого языка Rust, т.к. нет никаких фундаментальных препятствий сгенерировать меньший набор инструкций (как это делает CLANG).


Заключение


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


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


  1. amarao
    18.09.2019 16:17
    +8

    Rust сложный язык. Дело не в lifetimes, а в логике предикатов, возникающих на generic'ах у трейтов (тайп-параметры). inner mutability добавляет веселья.


    Любая попытка использовать Rust должна идти с точным пониманием, что вся восхитительность высокоуровневого кода и гарантий Rust достигается за счёт удовлетворения (программистом) кода, проверяющего lifetime и вычислимость функций на типах. Это сложно. Начать писать на rust просто, научиться писать на rust сложно. Не сложнее, чем на C++, конечно, но сложно.


    В сравнении с С/С++ я могу сказать одно — Rust (стабильный) не умеет обрабатывать ошибки аллокации. Пока эту проблему не решат, он не может быть настоящим системным языком.


    А вот насчёт unsafe… Ух, не трогайте эту гидру. Можно иметь такой unsafe-инваривант, который сделает UB в safe-коде, хотя сам unsafe блок совершенно невинный. Про это много говорили в курсе лекций по Rust на computer science center (https://www.youtube.com/playlist?list=PLlb7e2G7aSpTfhiECYNI2EZ1uAluUqE_e).


    1. Gymmasssorla Автор
      18.09.2019 16:28

      Ошибки аллокации можно попытаться отлавливать посредством своего хендлера паники или кастомного аллокатора. Это, конечно, очень дубовый подход, я бы предпочёл возвращать Result<Vec, AllocError> из методов Vec::new/Vec::with_capacity/etc.


    1. mayorovp
      18.09.2019 16:33
      +2

      Можно иметь такой unsafe-инваривант, который сделает UB в safe-коде, хотя сам unsafe блок совершенно невинный.

      Поправка: выглядит невинным. UB всё равно при этом происходит в нём.


      1. amarao
        18.09.2019 18:28
        +2

        Нет. В этом и драма unsafe, он может что-то (очень даже defined) поменять в логике, в результате чего UB происходит в safe коде. Фигурные скобочки вокруг unsafe — это ложная идея, unsafe показывает места, где код что-то делает, но не места, где надо быть внимательным к UB. UB может происходить чёрти где, не имеющем никакого отношения в unsafe коду.


        Я дал ссылку на плейлист, лекция 11, там много примеров.


        1. PsyHaSTe
          18.09.2019 18:31
          +3

          Да нет, все UB всегда в фигурных скобочках. Например, можно собрать строку из невалидного UTF8, и в сейф коде при попытке поработать с этой строкой будет паника. Однако проблема не в коде читающем невалидную строчку, а в том, который её сгенерировал.


          Упасть может любая строчка, включая сейф. Но причина падения всегда будет обернута в unsafe{}


          1. amarao
            18.09.2019 18:44
            +1

            Давайте договоримся, что UB — это undefined behavior, а не "всё плохое". Например, если кто-то вам положит невалидный указатель в структуру, это не UB. UB — чтение по такому указателю.


            1. a1ien_n3t
              18.09.2019 18:56
              +1

              Вобщето нет. Факт чтения не обезателен для получения UB.
              Например

              let x = unsafe{  std::mem::uninitialized::<bool>() };
              

              Этот код UB


              1. amarao
                18.09.2019 19:41

                Да, вызов std::mem::uninitialized — undefined behavior. Однако, например, запись в указатель null — это не UB. А вот если какая-то редиска попытается это читать (вне unsafe блока, полагая, что там вменяемые данные), то получится "Dereferencing a null or dangling raw pointer." Писать можно — читать нельзя.


                1. mayorovp
                  18.09.2019 19:50
                  +4

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


                  Если же вы имели в виду не сырой указатель, а нормальную ссылку — то она не может указывать на null. Создание ссылки на null как раз и нарушает языковой инвариант: "ссылка никогда не указывает на null".


                  Это, кстати, общий инвариант для Rust и C++.


                  1. technic93
                    18.09.2019 21:24

                    но программа упадёт при чтении по ссылке а не при её создании


                    1. vitvakatu
                      18.09.2019 21:38
                      +1

                      Какая разница, когда программа упадет? Открою страшную тайну — она может упасть когда угодно, позвонить президенту, вызвать инопланетян или духа Чарльза Бэббиджа. Именно так работает UB и в Rust, и в С++. Если в ссылке оказался null — это уже UB, и если компилятор достаточно (не) умен, чтобы заставить программу падать при чтении, а не делать все вышеперечисленное — можете тихо порадоваться в душе и никогда больше не писать такой код.


                      1. technic93
                        18.09.2019 21:51

                        Смотря как определяете UB, висячая ссылка это уже UB? Только в раст? А в С++ нет?


                        1. PsyHaSTe
                          18.09.2019 21:52
                          +1

                          1. technic93
                            18.09.2019 21:56
                            -1

                            Мне выше пишут


                            Если в ссылке оказался null — это уже UB

                            А по ссылке


                            Dereferencing a null or dangling raw pointer.

                            Про это разницу и говорю


                            Upd. Нет там таки стоит:


                            Invalid values in primitive types, even in private fields and locals:
                            • Dangling or null references and boxes.


                            1. vitvakatu
                              18.09.2019 21:58

                              Не пробовали дочитать до пункта "Dangling or null references and boxes."? И я уже не говорю что этот список не полный.


                              1. technic93
                                18.09.2019 22:01

                                окей тогда в расте и в С++ официальные понятие UB отличаются. Т.е. согласно доке у них вернуть плохое из unsafe уже называется UB. Но в реальности ничего не будет пока мы к этому не начнем обращатся или как то взамодействовать из другой части кода.


                                1. Revertis
                                  19.09.2019 11:40

                                  Поэтому Rust и считается более безопасным.


                                  1. Aldrog
                                    19.09.2019 12:09

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


                                    1. Revertis
                                      19.09.2019 12:15

                                      Так пишите без UB! Кто вас вообще заставляет писать используя unsafe?


                                      1. Aldrog
                                        19.09.2019 12:22

                                        Я вообще не пишу на Rust, только интересуюсь :)
                                        Как мне кажется, выделение unsafe блоков — как раз очень полезная с точки зрения безопасности фича. А мой комментарий выше — контраргумент к Вашему утверждению, что Rust безопасен тем, что UB проявляется (не)определённым образом.


                                1. Aldrog
                                  19.09.2019 12:07

                                  В C++ тоже сплошь и рядом UB проявляется гораздо позже, чем происходит. Например, у нас недавно был случай, когда приложение начинало зависать через пару часов работы после того, как объект был удалён по указателю на интерфейс, в котором не было виртуального деструктора.


                                  1. technic93
                                    20.09.2019 11:57
                                    -1

                                    такой варнинг вам любой компилятор выдает.


                                    1. Aldrog
                                      20.09.2019 13:38

                                      Во-первых, мне почему-то ни gcc -Wall -Wextra -Wpedantic, ни даже clang-tidy ничего не сказали, а во-вторых мой комментарий был вообще не об этом.


                                      1. technic93
                                        20.09.2019 14:25

                                        Я понял посыл вашего комментария, просто удивился что у вас такая ошибка скомпилировалась. Видел такие предупреждения в QtСreator в своём коде иногда (с настройками clang по умолчанию от Qt). Интересно или у Вас компилятор старый или у Вас какой то специфичный случай в коде что компилятор не отловил, тогда можно и баг зарепортить. Вроде -Wall -Wextra должно хватать для отловки delete, eщё есть -Wnon-virtual-dtor.


                                        1. 0xd34df00d
                                          20.09.2019 16:12

                                          Компиляторы это стали репортить относительно недавно.


                                        1. Aldrog
                                          20.09.2019 16:38
                                          +1

                                          gcc 9.1.0
                                          С -Wnon-virtual-dtor действительно ловит, -Wall -Wextra -Wpedantic оказывается недостаточно.


                                          1. technic93
                                            20.09.2019 18:31

                                            Есть ещё -Wdelete-non-virtual-dtor который входит в -Wall и он должен ругаться на строчку с delete.


                                            1. Aldrog
                                              20.09.2019 19:28

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


                                              1. technic93
                                                20.09.2019 19:38

                                                clang выдаёт, gcc нужен флаг -Wsystem-headers потому что ошибка по сути там возникает.


                            1. PsyHaSTe
                              18.09.2019 22:01
                              +1

                              Вы понимаете разницу между pointer и reference?


                              1. technic93
                                18.09.2019 22:02
                                +1

                                Да. Сразу не заметил что там про другое.


                      1. screwer
                        19.09.2019 19:35
                        +1

                        Падение при чтении по null это заслуга не компилятора, а ОС.


                        1. Siemargl
                          19.09.2019 22:08

                          вообще то можно не падать… есть варианты с диагностикой


                          1. 0xd34df00d
                            20.09.2019 16:12

                            Не падать нельзя, упасть придётся (ну или экзепшон кинуть, который обработать принципиально умнее, чем в main через catch (...), у вас не получится).


                            1. Siemargl
                              20.09.2019 17:19

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


                1. mkpankov
                  20.09.2019 09:09

                  Вообще это интересно, что вы коснулись mem::uninitialized. Как раз с ним был связан недавний RFC — эта функция теперь deprecated.
                  А почему? А как раз потому, что, как оказалось, факт создания значения определённого типа вне области определения этого типа — уже UB.
                  Это всё важно в первую очередь в контексте оптимизации. Если вы компилятор, и вам говорят, что вот тут bool — допустимые значения 0 и 1, то встретив код типа


                  let flag = false;
                  let x = 100500;
                  ...
                  if flag {
                    x += 1;
                  }

                  вы можете соптимизировать этот код, основываясь на численном представлении bool. Если bool не 0 или 1, этот код будет работать неправильно. Но не только этот код. Возможно этот bool используется уже скомпилированным кодом стандартной библиотеки, например.
                  Поэтому да, странные эффекты могут начаться по всей программе. Но UB всё равно именно там, где нарушили инвариант — в unsafe.


                  1. DreamingKitten
                    20.09.2019 14:23

                    Извините за возможно тупой вопрос, но разве true это не non-zero value именно в определении булевского типа?


                    1. vitvakatu
                      20.09.2019 15:46

                      Нет, в Расте внутреннее представление bool не специфицировано. Но если скастовать его к integer, будет либо 0 либо 1:


                      The bool represents a value, which could only be either true or false. If you cast a bool into an integer, true will be 1 and false will be 0.


                      1. DreamingKitten
                        20.09.2019 16:03

                        а если скастовать 2 в bool, что будет?


                        1. vitvakatu
                          20.09.2019 16:23
                          -1

                          Undefined behavior, то есть все что угодно.


                          1. vitvakatu
                            20.09.2019 16:37
                            +1

                            Для тех, кто мне не верит, почитайте https://doc.rust-lang.org/reference/behavior-considered-undefined.html, конретно пункт "A value other than false (0) or true (1) in a bool."


                        1. mayorovp
                          20.09.2019 16:31

                          Вроде бы ошибка компиляции. Нельзя целые числа в булев тип кастовать.


                          1. vitvakatu
                            20.09.2019 16:36

                            А вы через unsafe { std::mem::transmute(x) } попробуйте :)


                        1. red75prim
                          20.09.2019 16:37
                          +1

                          В safe Rust в bool кастовать нельзя. Если скастовать с помощью unsafe { mem::transmute(2) }, то будет UB. Оба соседних ответа частично правильны.


                          1. PsyHaSTe
                            21.09.2019 09:47

                            ИМХО "Можно, но вызовет UB" равноценно "нельзя".


                      1. PsyHaSTe
                        21.09.2019 09:43

                        Сравнительно специфицированно:


                        On all currently-supported platforms bool has only two valid values, true (bit-pattern: 0x1) and false (bit-pattern: 0x0).

                        mem::uninitialized, however, produces a bit-pattern where all bits have the value uninitialized. This bit-pattern is neither 0x0 nor 0x1, therefore, the resulting bool is invalid, and the behavior is undefined.

                        To make the behavior defined we would need to change the definition of bool to support three valid values: true, false, or uninitialized. We can't, however, do that, because T-lang and T-compiler already RFC'ed that bool is identical to C's _Bool and we can't break that guarantee (this allows bool to be used portably in C FFI).


                    1. lain8dono
                      20.09.2019 15:53

                      std::mem::uninitialized — это не заполнение нулями, UB — это не только "заполнить чем угодно" даже в данном случае. UB — это "сделать что угодно и где угодно без каких либо гарантий вообще". Для заполнения нулями есть std::mem::zeroed. Но вообще для этих целей есть std::mem::MaybeUninit. Это более корректный вариант более ограниченного UB, но это всё хаки для оптимизаций. Для корректного zero value есть std::default::Default.


                      Вот вещи, которые теоретически могут произойти с UB на uninitialized:


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

                      Всё это верно для любого unsafe блока в Rust, а также для всего C/C++ кода.


                      1. PsyHaSTe
                        21.09.2019 09:49

                        Код с UB просто не является программой на расте, вот и все. Любителей ускорить код через UB наверное с распростертыми объятиями ждут где-нибудь в линуксе.


                        1. MikailBag
                          21.09.2019 23:22

                          Код с UB просто не является программой на расте.
                          Почему? Rustc не отклоняет ее, значит это корректная прогорамма (несмотря на то что ее поведение во всех случаях не определено).


                          1. PsyHaSTe
                            21.09.2019 23:36
                            +1

                            Почему? Rustc не отклоняет ее, значит это корректная прогорамма

                            Нет, не означает. То же свойство UB у любого другого языка. Почитайте вот тут, особенно раздел shifting responsibility. Спека запрещает UB в любом виде, поэтому если в программе написанной на Си есть UB то оно не является программой на си в строго смысле этого слова. Тоже самое в случае раста.


            1. red75prim
              18.09.2019 19:02
              +3

              Не стоит запутывать. Нарушение правил написания unsafe кода может привести к UB, вот и всё. Нарушение инвариантов, на которые опирается safe код, — это, естественно, нарушение правил написания unsafe кода.


              Если таких нарушений нет, safe код UB вызвать не может (если вызывает — это баг компилятора, и он будет исправлен).


              1. amarao
                18.09.2019 19:43
                -1

                Я, собственно, эту мысль и доносил — unsafe код может вызывать UB вне unsafe блока. Внутри unsafe всё будет правильно и с инвариантами, но будут нарушаться инварианты safe-кода.


                1. PsyHaSTe
                  18.09.2019 21:52

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


                1. 0xd34df00d
                  19.09.2019 02:44

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


            1. mayorovp
              18.09.2019 19:15
              +3

              UB — это то, чего никогда нельзя допускать в корректной программе, потому что в противном случае компилятор за корректность программы ответственности не несёт.


              Нарушения языковых инвариантов нельзя допускать.


              1. amarao
                18.09.2019 19:42
                -5

                Тогда у меня для вас печальная новость, ваша программа не может делать IO. Любое IO — undefined behavior, и по понятным причинам может вызывать странные эффекты вне того кода, который его вызывал.


                1. mayorovp
                  18.09.2019 19:48
                  +5

                  Нет. Любое IO не является UB, его поведение вполне определено. В частности, ввод-вывод не нарушает языковые инварианты.


                1. PsyHaSTe
                  18.09.2019 21:54
                  +5

                  Любое IO — undefined behavior

                  Мне начинает казаться, что вы не знаете, что такое UB.


          1. technic93
            18.09.2019 21:39

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


            1. PsyHaSTe
              18.09.2019 21:55
              +2

              Так и в расте можно наворотить в ансейф дичь (нарушив инварианты) и вернуть оттуда якобы правильную структуру но при первом обращении к структуре в сейф коде программа упадёт

              Так в этом-же и фишка. УБ не в том месте, которое читает структуру, а в том, которое структуру создало в неправильно состоянии. А создать структуру в неправильном состоянии можно только через ансейф. В итоге если у вас что-то странное происходит в программе можно смело грепать по unsafe и в 100% случаях вы найдете, где что сломалось.


        1. cpud36
          21.09.2019 17:00

          В том примере unsafe не совсем корректен(в строгом смысле).

          Поскольку unsafe помимо возможностей добавляет обязательства. А именно: какой бы странный safe код не был вокруг, он не должен вызывать UB[1].

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

          Вообще, поскольку unsafe кода мало, то способы его корректного написания развиваются медленнее, чем хотелось бы.

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

          [1] UB может возникать в довольно неожиданных ситуациях. Так-то, создание невалидной ссылки уже вызывает UB. Тем не менее, если UB есть, то его проявления могут быть как после его возникновения, так и до.


    1. humbug
      18.09.2019 17:36
      +11

      В сравнении с С/С++ я могу сказать одно — Rust (стабильный) не умеет обрабатывать ошибки аллокации. Пока эту проблему не решат, он не может быть настоящим системным языком.

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


      Зато если хотите низкоуровщины, типа драйверов под линукс или писать под голое железо (Monotron под TM4C123 80MHz 256KiB flash 32KiB sram, человек реализовал софтовый VGA, в видосике показывает работу интерпретатора бейсика и игру змейка), попробуйте #[no_std]. Там у вас не будет ошибок аллокаций, потому что аллокаций нет вообще (закадровый смех Джастаса Уолкера). И мейна у вас нет. Куда уж низкоуровневей и системней.


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


    1. JekaMas
      18.09.2019 19:19
      +6

      Для меня основное препятствие для внедрения rust в прод — это цена написания кода и то, что если я могу взять программиста с php, python, java c# и дать ему даже сложный код на go, коль уж его тут упоминали, то закрытые задачи я получу через пару недель максимум. Да и переход с динамических яп на c#, java, golang намного проще, чем такой же переход на rust.
      К сожалению, основная аудитория — это всё еще c++ разработчики. Но если мне нужна производительность, а rust может предложить 10-40% лучше в отдельных задачах, а в отдельных задачах хуже, то вопрос сводится к деньгам — какова цена внедрения rust, насколько замедлится после разработка, насколько усложнится найм, насколько затянется введение новых ребят в проекты? И что мы получаем взамен.
      Я пока не смог дать однозначного ответа на эти вопросы и остановился на том, что в текущем состоянии rust очень интересный и очень неоднозначный.


      1. mayorovp
        18.09.2019 19:25
        +1

        Переход с c# на rust проще чем на go.


        1. PsyHaSTe
          18.09.2019 21:57

          Это факт. В расточате б0льшая часть растовиков, как ни странно шарписты и питонисты. Плюсовиков не так уж много.


          1. skrimafonolog
            19.09.2019 00:27

            Это факт. В расточате б0льшая часть растовиков, как ни странно шарписты и питонисты. Плюсовиков не так уж много.

            Это связано не только с языком, а еще и с тем где ты его используешь, где работаешь.


      1. amarao
        18.09.2019 19:46
        +1

        Rust очень многообещающий, но всё ещё очень молодой. Достаточно сказать, что срок жизни релиза компилятора — 6 недель. А потом его перестают поддерживать.


        Миграция с современной java и C#, я думаю, будет за разумные усилия. С php/python — уже сложнее, сильно сложнее.


        В принципе, у Rust сейчас есть конкретное преимущество перед Java/C#/Go — это lifetimes, позволяющие чуть больше проверять компилятором для многопоточного кода.


        1. JekaMas
          18.09.2019 20:07

          С другой стороны lifetimes не отменяет необходимости тестов, в которых и другие языки могут те же гонки отловить.


          1. Jouretz
            18.09.2019 21:02
            +2

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


            1. vitvakatu
              18.09.2019 21:31

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


              Гонку данных невозможно предотвратить средствами языка, потому что это сугубо логическая ошибка программиста. Раст тут части помогает с borrow checker'ом, но это не панацея.


            1. JekaMas
              18.09.2019 21:31
              +1

              Ну у той же гошки есть race detector, у java тоже встречал инструменты.
              E2e тесты всё равно нужны и на их уровне гонки и отлавливать.


              1. Jouretz
                18.09.2019 22:59

                Так, стоп. Race detector, как я понимаю, к тестам отношения не имеет, он лепится в рантайм и репортит сам факт возникновения состояния гонки, которого в тестах может и не произойти.
                Так что какие-то гарантии оно даёт, но далеко не стопроцентные. Понятно что в зависимости от области применения этого может быть достаточно, но это таки не проверка гонки тестом


                1. JekaMas
                  18.09.2019 23:15

                  В go он лепится и к тестам добавлением флага -race.
                  И собственно дальше любимый инженерный вопрос, что мы хотим и какую метрику оптимизируем: условно иметь разработку за 1 ед. времени и 97+% вероятности отсутствия гонки, или 100% уверенности в отсутствии гонки и цену в 5 ед.


                  Но для этих 97% не надо дополнительных усилий. E2e тесты писать надо на любом языке.


                  1. PsyHaSTe
                    19.09.2019 01:10

                    Это из разряда "мы пишем на жс, а на то что в функу не придет строка вместо числа пишем тесты".


                    Ваше утверждение корректно, но в случае с растовыми типами для многопотока имеем скорее 1ед за 70% уверенности, и 1.1 ед за 100% уверенности.


                    1. JekaMas
                      19.09.2019 12:43

                      Мы не знаем этих величин на практике. Может оказаться и 10 ед раста за 95% уверенности на 1 ед. другого языка за 90% уверенности.
                      Поэтому я и говорю «условно». Точный ответ надо получать с накоплением опыта разработки, желательно в больших компаниях, где одинаковые требования к разработчикам, одни бизнес-процессы, но разные сервисы живут на разных ЯП. AliBaba была бы прекрасна для подобного. До этого мы лишь уверенны в самой общей формулировке «раст стоит дороже в разработке и дает 100% уверенности насчет многопоточного кода; альтернативные языки чаще всего стоят дешевле и дают насколько-то меньше уверенности».


                      1. KanuTaH
                        19.09.2019 12:51

                        Rust отнюдь не даёт "100% уверенности" насчёт многопоточного кода, он борется только с одним видом проблем в многопоточном коде — data races, и это comes for a price, а именно невозможность иметь более одной mutable reference на один объект в один момент времени.


                        1. red75prim
                          19.09.2019 13:18

                          а именно невозможность иметь более одной mutable reference на один объект в один момент времени

                          Точнее для этого обязательно требуется явная синхронизация: UnsafeCell + механизм синхронизации доступа, RefCell, Cell, Mutex, RwLock и т.п.


                          1. KanuTaH
                            19.09.2019 13:20

                            Ну да, только через прослойку такого рода.


                            1. red75prim
                              19.09.2019 14:17

                              Но и в других языках механизмы для исключения data races тоже come with a price. Явные/неявные memory barriers, атомики, те же мутексы.


                              1. KanuTaH
                                19.09.2019 14:19

                                Да, но здесь мутексы тоже никуда не деваются, а невозможность иметь более одной mutable reference остается (unsafe в расчет не берем) даже если никаких потоков и в помине нет.


                                1. mayorovp
                                  19.09.2019 14:25

                                  А это полезная (не)возможность.


                                  1. KanuTaH
                                    19.09.2019 14:26

                                    Ну да, «потребности в колбасе» вроде многосвязных графоподобных структур на сегодня нет. Для вас, может, и полезная, а для меня — не очень.


                                1. red75prim
                                  19.09.2019 16:31

                                  Почему не деваются? Типы, предназначенные для использования в одном потоке (не реализующие трейт Sync), не могут использоваться с Mutex. Shared mutability для них реализуется через Cell и RefCell, которые не используют никаких примитивов межпоточной синхронизации.


                                  Так что речь может идти только о цене для программиста. Использование *mut — тоже один из вариантов, где цена для программиста не сильно выше, чем других языках (дополнительно нужно обеспечить отсутствия алиасинга с существующими ссылками, и не забывать про zero sized типы).


                                  Естественно, речь идёт про однопоточный вариант. Проблема shared mutability в многопоточном коде ещё не решена. Например: A Promising Semantics for Relaxed-Memory Concurrency


                                  1. KanuTaH
                                    19.09.2019 16:46

                                    Я имею в виду, что невозможность иметь более одной mutable reference в один момент времени без подпорок типа Cell никуда не девается даже в однопоточном коде. И накладные расходы с этими Cell, насколько я понимаю, тоже вполне себе есть — например, RefCell ведет подсчет «одалживаемых» ссылок в runtime, и в runtime же паникует, если вдруг ты пытаешься сделать больше одного borrow_mut() одновременно.


                                    1. PsyHaSTe
                                      19.09.2019 17:00

                                      Иметь больше одной mut ссылки — плохо, не надо так делать. То, что язык это запрещает — хорошо.


                                      1. KanuTaH
                                        19.09.2019 17:08

                                        Такое религиозное рвение, конечно, похвально, но, как кто-то сказал про тот же Cell, «this is necessary because of a frustrating implementation issue called „the real world“» :) То есть вот такой код в растике не скомпилируется:

                                        let mut x = 1;
                                        let y = &mut x;
                                        let z = &mut x;
                                        x = 2;
                                        *y = 3;
                                        *z = 4;
                                        


                                        а вот такой — скомпилируется и заработает:

                                        use std::cell::Cell;
                                        
                                        let x = Cell::new(1);
                                        let y = &x;
                                        let z = &x;
                                        x.set(2);
                                        y.set(3);
                                        z.set(4);
                                        


                                        Почему, спросите вы, они же по сути эквивалентны? А потому что чьи-то догмы не выдержали столкновения с реальным миром, и родил он Иуду и братьев его UnsafeCell, Cell и RefCell :)


                                        1. red75prim
                                          19.09.2019 17:18

                                          А C родил restrict, чтобы догнать фортран.


                                          1. KanuTaH
                                            19.09.2019 17:21

                                            Ну, не знаю, ставилась ли цель именно «догнать и перегнать фортран», но то, что restrict увеличивает простор для оптимизаций компилятора — это факт :)


                                            1. Gymmasssorla Автор
                                              19.09.2019 17:25

                                              Си раньше соперничал с Fortran. Поэтому и были введены массивы переменной длины, complex.h и, собственно, restrict.


                                            1. red75prim
                                              19.09.2019 17:36

                                              Да, и &mut T — это аналог T restrict * в C.


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


                                              fn xor_swap(a: &mut u32, b: &mut u32) {
                                                  *a ^= *b;
                                                  *b ^= *a;
                                                  *a ^= *b;
                                              }

                                              и мало ли что ещё. Зато не надо писать Cell или unsafe.


                                              1. KanuTaH
                                                19.09.2019 17:44

                                                Ну я как-то не уверен, что restrict в C используют чаще, чем Cell в Rust. Я почему-то думаю, что все как раз наоборот. Но это лично мое мнение, не подтвержденное статистикой :)


                                                1. red75prim
                                                  19.09.2019 17:49
                                                  +1

                                                  Дык. Шаг в сторону — UB, прыжок на месте — silent error.


                                                1. PsyHaSTe
                                                  19.09.2019 20:12
                                                  +1

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


                                                  1. KanuTaH
                                                    19.09.2019 20:15

                                                    Вообще-то именно в плюсах restrict нет, ни в одном стандарте. Это фишка чисто из C99, в плюсах есть только в составе расширений. А не используют потому, что это нужно относительно редко. А вот Cell в растике приходится использовать куда чаще, потому что вот без него реально «ни туды, ни сюды», несмотря на декларируемую некоторыми парадигму «одного mut ref ought to be enough for anybody».


                                                    1. PsyHaSTe
                                                      19.09.2019 21:27

                                                      А не используют потому, что это нужно относительно редко.

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


                                                      А вот Cell в растике приходится использовать куда чаще, потому что вот без него реально «ни туды, ни сюды», несмотря на декларируемую некоторыми парадигму «одного mut ref ought to be enough for anybody».

                                                      Написал бота для телеграмма без единого Cell. Чяднт?


                                                      есмотря на декларируемую некоторыми парадигму «одного mut ref ought to be enough for anybody».

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


                                                      1. KanuTaH
                                                        19.09.2019 21:32
                                                        +1

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

                                                        Если это дает какой-то ощутимый выигрыш в данном месте, то используют.

                                                        Написал бота для телеграмма без единого Cell. Чяднт?

                                                        Ну, раз лично вы написали целого бота для телеграма без единого Cell, значит, он не нужен, это же логично. Надо выкинуть его из std :)

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

                                                        Зачем тогда вообще раст, если все можно прекрасно писать на хаскелях? Или все-таки не все? :)


                                                        1. 0xd34df00d
                                                          19.09.2019 21:48

                                                          Если это дает какой-то ощутимый выигрыш в данном месте, то используют.

                                                          Я не использую UB (типа reinterpret_cast данных, вычитанных из сокета или mmap), ни при каком выигрыше вообще. Вещи, которые очень легко превратить в UB, я использую только при очень серьезных причинах. restrict — одна из таких вещей, потому что компилятор нихрена не гарантирует.


                                                          Тут, кстати, можно порассуждать о близости плюсов к железу, но то такое.


                                                          1. KanuTaH
                                                            19.09.2019 21:54

                                                            Если использование restrict дает в данном месте серьезный выигрыш в плане производительности, то это вполне может сойти за «серьезную причину». Но используют его редко и при наличии серьезных оснований, это да. Еще Кнут говорил, что преждевременная оптимизация — корень всех зол, поэтому всерьез говорить, что всеобщий автоматический restrict — это хорошо (в обмен на костыли для куда более часто встречающихся случаев) — это такое себе.


                                                            1. 0xd34df00d
                                                              20.09.2019 16:15

                                                              Для того, чтобы понять, какой выигрыш даёт restrict в данном конкретном месте, мне надо


                                                              1. либо увидеть, что это горячее место, чтобы на него смотреть внимательно (успехов, если профайл равномерно размазан по куче функций, что бывает, увы, нередко),
                                                              2. либо смотреть на дизасм всего подряд,
                                                              3. либо добавлять restrict везде, что ли, и надеяться на духа Страуструпа.

                                                              Все варианты какие-то ненадёжные.


                                                        1. PsyHaSTe
                                                          19.09.2019 22:12

                                                          Зачем тогда вообще раст, если все можно прекрасно писать на хаскелях? Или все-таки не все? :)

                                                          Если важна производительность и латентность, конечно же. А вообще посыл верный.


                                                          Если это дает какой-то ощутимый выигрыш в данном месте, то используют.

                                                          А часто ли кто-то вообще его бенчмаркает? Возможно он дал бы выигрыш, да только никто не замерял.


                                                          1. KanuTaH
                                                            19.09.2019 23:18

                                                            А часто ли кто-то вообще его бенчмаркает?

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

                                                            P.S. Все эти размышления о мегапользе от авторестрикта напоминают мне историю от какого-то товарища (не помню, где я читал, возможно, даже здесь, на Хабре), как он оптимизировал свой js-движок для своей игры. Без всякого профилинга он решил, что он сильно выиграет, если перейдет на некую библиотеку по «быстрой» работе с dense arrays (у него все массивы были dense), потому что вот мол он много работает с массивами. На синтетических тестах это давало выигрыш там чуть ли не в два раза, он радостно переписал кучу кода, потом переписал саму библиотеку, но… на выходе на реальных задачах выигрыша не получил. Отсюда мораль: преждевременная оптимизация, особенно с применением велосипедов — зло.


                                                            1. PsyHaSTe
                                                              19.09.2019 23:26

                                                              Вполне вероятно, что расстановка restrict по всей программе ускорила бы её на 10-20%, но это не стоит миллионов человекочасов на реализацию… Если только оно не идет бесплатно от компилятора.


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


                                                              1. KanuTaH
                                                                19.09.2019 23:27

                                                                Я тут постскриптум написал на тему :) «Вполне вероятно» — это не есть аргумент.

                                                                P.S. А, ну да, вот:

                                                                habr.com/ru/company/ruvds/blog/465809

                                                                :)


                                                                1. PsyHaSTe
                                                                  20.09.2019 11:12

                                                                  Так это не преждевременная оптимизация, а бесплатный сыр от компилятора, вроде векторизации циклов


                                        1. PsyHaSTe
                                          19.09.2019 17:30

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


                                          1. math_coder
                                            19.09.2019 20:57

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


                                        1. 0xd34df00d
                                          19.09.2019 17:35
                                          +1

                                          То есть вот такой код в растике не скомпилируется:

                                          Если бы такой код компилировался, то код вида


                                          x = 2;
                                          y = w;
                                          let z = x;

                                          оптимизировать в let z = 2 было бы очень нетривиально.


                                          Это короче как alias analysis, только на порядки мощнее.


                                          А, выше что-то похожее уже написали, собственно.


                                        1. mayorovp
                                          19.09.2019 17:59

                                          Зачем спрашивать-то? Очевидно же, что во втором примере явно разрешено изменение значения по разным ссылкам, о чём и указано в его типе.


                                          1. KanuTaH
                                            19.09.2019 18:03

                                            Ну мне же пишут в зелотском стиле: «иметь больше одной mut ссылки — плохо, не надо так делать, то, что язык это запрещает — хорошо», а во втором примере оказывается, что это не только не «плохо», а даже частенько «необходимо» :) А поскольку это таки «необходимо», то и появились закономерные костыли типа Cell.


                                            1. mayorovp
                                              19.09.2019 19:40
                                              +1

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


                                              1. Siemargl
                                                19.09.2019 22:12

                                                ну так они есть и в С++ и в Расте (по умолчанию)

                                                а restrict [пока что] не дает никакого выигрыша


                                        1. mkpankov
                                          20.09.2019 09:19

                                          И заработает, да. Потому что тут у вас один поток исполнения.
                                          Это хорошо, что вас заставляют специально извернуться, чтобы указать, что вы собираетесь работать с куском памяти однопоточно. Когда по умолчанию считается, что можно, получается небезопасно.
                                          И да, тут у вас всё равно остаётся механизм защиты, который можно описать как "run-time borrowing". К Cell это не применимо, т.к. там только Copy-типы, но RefCell паникует, если вы таки взяли одновременно 2 ссылки на запись.


                      1. PsyHaSTe
                        19.09.2019 13:23

                        Мы не знаем этих величин на практике. Может оказаться и 10 ед раста за 95% уверенности на 1 ед. другого языка за 90% уверенности.

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


                        1. JekaMas
                          19.09.2019 13:26

                          Если переформулировать, то все же будет «для меня в моём текущем проекте цена поддержки такой гарантии дешевле чем её тестирование, но я не считал».


                          1. PsyHaSTe
                            19.09.2019 13:29

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


                            1. JekaMas
                              19.09.2019 13:30

                              То есть считали?


                              1. PsyHaSTe
                                19.09.2019 13:32

                                Я считал, насколько мне надо париться чтобы сделать многопоточную структуру на дотнете (много) — нужно помнить про volatile, как lock взаимодействует с async/await, вот это все, и на расте (мало). В итоге на расте я сделал быстрее аналогичный сервис, чем на сишарпе. И голову напрягал меньше — все несовместимые вещи мне компилятор подсказал.


                                1. JekaMas
                                  19.09.2019 13:41

                                  Вы один? Команды не было?


                                  1. PsyHaSTe
                                    19.09.2019 13:44

                                    Нет, я писал свой маленький компонент.


                    1. JekaMas
                      19.09.2019 12:45

                      Кстати, может вы знаете, не нашел пока ни у кого объяснения, но может мне со стороны его найти трудно: по графику LoC кода в Mozilla rust не меняется последний год, то есть его доля падает, поскольку объем кодовой базы растёт. Не знаете причин этого?


                      1. vitvakatu
                        19.09.2019 12:51

                        А где можно увидеть этот график? Можно попробовать узнать у сотрудников Mozilla.


                        1. PsyHaSTe
                          19.09.2019 13:24

                          У меня есть только такая статистика: https://4e6.github.io/firefox-lang-stats/


                          1. JekaMas
                            19.09.2019 13:49

                            Выше ответил



                        1. lain8dono
                          22.09.2019 00:16

                          Можно попробовать узнать у сотрудников Mozilla.

                          https://habr.com/ru/users/kvark/
                          Не благодарите. Он далеко не последнюю роль играл в появлении Firefox Quantum.


        1. vitvakatu
          18.09.2019 21:33
          +2

          Я часто сталкиваюсь с людьми, которые говорят о 6-недельном цикле релизов как о негативном факторе. Можно узнать, почему? Как мне кажется, это гораздо лучше многолетних циклов. Баг фиксы приходят быстрее, быстрее тестируются, быстрее появляются новые полезные фичи.


          1. JekaMas
            18.09.2019 23:15

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


            1. vitvakatu
              18.09.2019 23:18
              +3

              Зачем поддерживать прошлые версии, если они все в общем случае обратно совместимы?


              1. JekaMas
                19.09.2019 13:27

                Всегда?


                1. vitvakatu
                  19.09.2019 13:46
                  +1

                  После версии 1.0, на текущий момент, да.


            1. defuz
              18.09.2019 23:42

              А вы посмотрите на это с другой стороны. Rust 2015 edition вышел в далеком 2015 году и за 4 года вышло целых 37 патчей.

              Rust 2018 edition уже stable, но компилятор поддерживает одновременно обе редакции языка, и будет поддерживать 2015 еще очень долго.


        1. mkpankov
          20.09.2019 09:14

          А у вас какие-то особые требования, по которым вам надо сидеть на одной заранее выбранной версии компилятора?
          Такое бывает, например, при сертификации, но в обычном продакшене вы просто обновляетесь раз в 6 недель на новый компилятор. Максимум что он привнесёт — warning'и о deprecation чего-либо. Чините их, и на следующую версию опять обновляетесь без проблем.
          Обратную совместимость уже 4 года как не ломают.


          1. Siemargl
            20.09.2019 10:07
            -1

            Обратную совместимость уже 4 года как не ломают.
            Да неужели? Три месяца назад
            И, как результат, NLL сейчас находится на стадии «миграции», в которой мы будем выдавать предупреждения вместо ошибок в том случае, если контроллер ссылок NLL не одобрит код, который бы одобрил старый контроллер ссылок на основе AST. Советуем взглянуть на список пострадавших публичных крейтов.


            1. mayorovp
              20.09.2019 10:17
              -1

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


              1. Cerberuser
                20.09.2019 10:22

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


                1. vitvakatu
                  20.09.2019 10:30

                  Времени на исправление более чем достаточно. mkpankov про это и писал в своем сообщении.


              1. Siemargl
                20.09.2019 11:05
                -3

                В кнопочку log стоит попробовать посмотреть. например

                [INFO] [stderr] error[E0505]: cannot move out of `_` because it is borrowed
                [INFO] [stderr] --> src/trees/binary.rs:164:19

                [INFO] [stderr] error[E0502]: cannot borrow `self.rows` as immutable because it is also borrowed as mutable
                [INFO] [stderr] --> src/main.rs:540:40

                [INFO] [stderr] error[E0597]: `lim` does not live long enough
                [INFO] [stderr] --> /opt/crater/cargo-home/registry/src/github.com-1ecc6299db9ec823/bcder-0.1.0/src/decode/content.rs:381:34
                итд


                1. mayorovp
                  20.09.2019 11:13
                  +1

                  Но ведь это лог запуска с намеренно выключенной совместимостью, а не с настройками по умолчанию!


                  1. mkpankov
                    20.09.2019 12:24
                    +1

                    Нет, все вокруг сговорились и обманывают Siemargl!


                  1. Siemargl
                    20.09.2019 17:08

                    где это видно из команды запуска?

                    [INFO] running `"docker" "create" "-v" "/mnt/crater-raid/crater/work/local/target-dirs/pr-60914/worker-4/try#f45cc3094ee337acd688771b9234318046b0572d:/opt/crater/target:rw,Z" "-v" "/mnt/crater-raid/crater/work/ex/pr-60914/sources/try#f45cc3094ee337acd688771b9234318046b0572d/gh/johshoff/find-tempo-rust-backend:/opt/crater/workdir:ro,Z" "-v" "/mnt/crater-raid/crater/work/local/cargo-home:/opt/crater/cargo-home:ro,Z" "-v" "/mnt/crater-raid/crater/work/local/rustup-home:/opt/crater/rustup-home:ro,Z" "-e" "USER_ID=1000" "-e" "SOURCE_DIR=/opt/crater/workdir" "-e" "MAP_USER_ID=1000" "-e" "CARGO_TARGET_DIR=/opt/crater/target" "-e" "CARGO_INCREMENTAL=0" "-e" "RUST_BACKTRACE=full" "-e" "RUSTFLAGS=--cap-lints=forbid" "-e" "CARGO_HOME=/opt/crater/cargo-home" "-e" "RUSTUP_HOME=/opt/crater/rustup-home" "-w" "/opt/crater/workdir" "-m" "1536M" "--network" "none" "rustops/crates-build-env" "/opt/crater/cargo-home/bin/cargo" "+f45cc3094ee337acd688771b9234318046b0572d-alt" "check" "--frozen" "--all" "--all-targets"`

                    ошибка явно не линта
                    [INFO] [stderr] error[E0503]: cannot use `separator` because it was mutably borrowed

                    ну и в любом случае новый NLL станет обязаловкой через пару версий


                    1. mayorovp
                      20.09.2019 17:20

                      Это из комментариев на гитхабе видно. А в командной строке параметра нет потому что там вообще исходный код модифицировали. Обратите внимание на PR (закрытые без слияния!), на которые есть ссылки из того сообщения, на которое дали ссылку вы.


                      1. Siemargl
                        20.09.2019 17:30
                        -2

                        -сначала утверждалось, что это поведение потому что запуск не с настройками по умолчанию…
                        -теперь утверждается, что это модифицированная версия компилятора

                        а смотреть я должен в корове, которая в яйце, в доме который построил джек

                        растаманы, вы что там курите?

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


                        1. mayorovp
                          20.09.2019 17:35

                          Ну так, по сути, настройки по умолчанию в компиляторе и менялись. Зачем так делалось — видимо, CI у них так работает: каждый PR прогоняется с настройками по умолчанию на всех опубликованных крейтах в поисках регрессий.


                        1. mkpankov
                          20.09.2019 19:34

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


                          1. Siemargl
                            20.09.2019 22:54
                            -2

                            И как это объясняет отсутствие заявленных опций «не по умолчанию» или «специальный» компилятор или прочую мистику, которую вы тут пытаетесь заявить?

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


                            1. mkpankov
                              21.09.2019 11:02

                              Описание PR из ваших же ссылок вы пробовали читать?
                              Что такое NLL migrate mode по вашему?
                              Смешно :D


                              1. Cerberuser
                                21.09.2019 12:25

                                И всё же. Допустим, имеется старая библиотека, которая сейчас получает warning-и из-за этого самого режима миграции. Позже, соответственно, перестанет компилироваться, даже в 2015 редакции. Использовать старую версию компилятора — не вариант, ввиду требований бизнеса (к примеру, как говорилось выше, — найденная уязвимость в генерируемом коде). Обновить библиотеку — не вариант, также ввиду требований бизнеса. Какое решение должно считаться корректным? Ну, кроме очевидного — принять, что подобные требования бизнеса вредят самому бизнесу, и всё-таки обновиться.


                              1. Siemargl
                                21.09.2019 23:26
                                -3

                                Я уже мягко намекнул выше, что нужны пруфы. От вас _нет никакой конкретики_ — ни одной ссылки!.. А отделаться отмазками не получится. В форуме по PR идут разночтения и обсуждение минимум 3х вариантов, в скрипте компиляции, что я привел — ничего нет, в плане нестандартных опций компиляции, в логе только упоминание про strict проверки линтера

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

                                ЗЫ, а что мне не понравилось, что теперь опасная (не компилирующаяся) программа выглядит так

                                 for part in buf.split(|x| { separator = x; (x == '<') || (x == '&') || (x == '\'') || (x == '"') }) {



                1. mkpankov
                  20.09.2019 12:24

                  Слышу звон, не знаю где он.


                  1. Siemargl
                    20.09.2019 17:22

                    И где? См выше


            1. mkpankov
              20.09.2019 10:39

              В общедоступном выпущенном компиляторе никогда не было NLL в запрещающем режиме. Т.е. это делалось в рамках разработки очередной версии с целью проверки того, собственно, можно ли его сразу в запрещающем режиме включать.
              Эти предупреждения ничем от deprecation warnings не отличаются.
              Дыры в soundness надо же как-то чинить.


          1. amarao
            20.09.2019 13:20
            +1

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


            Весь энтерпрайз на этом построен — старые версии должны работать.


            1. vitvakatu
              20.09.2019 13:22

              Кто вам мешает использовать старые версии компилятора и библиотек? Старые версии и продолжают работать.


              1. amarao
                20.09.2019 13:44
                +1

                Потому что в компиляторе может быть ошибка, в т.ч. уровня CVE. Новые библиотеки (которые нужны в проекте) могут хотеть новую версию компилятора. И нет, появление новой версии библиотеки, которая хочет новую версию компилятора не есть основание для исправления deprecation во всех остальных.


                Это требования энтерпрайза.


                1. mkpankov
                  20.09.2019 14:10

                  А, вот оно что.
                  Так всё-таки, в чём именно проблема? Берите новый компилятор и новые библиотеки. Упомянутые депрекейшены на практике правятся за пару минут.
                  Это реально не страшная цена за прогресс языка и эта ситуация точно лучше, чем в мире C++.


                  1. Cerberuser
                    20.09.2019 14:14

                    Депрекейшены в своём коде — да. Депрекейшены в давно не обновляющемся чужом коде — ой ли?


                    1. mkpankov
                      20.09.2019 19:23

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


                  1. KanuTaH
                    20.09.2019 14:15
                    +1

                    Так всё-таки, в чём именно проблема? [...] Упомянутые депрекейшены на практике правятся за пару минут.

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


                    1. Antervis
                      20.09.2019 14:16
                      +1

                      В «мире C++» так заботятся об обратной совместимости именно потому, что тут более реальные представления о стоимости правок старого кода.

                      в мире с++ просто другое соотношение «старого» и «нового» кода


                      1. KanuTaH
                        20.09.2019 14:18

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


                        1. Antervis
                          20.09.2019 14:29

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


                          1. KanuTaH
                            20.09.2019 14:34

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

                            www.cvedetails.com/cve/CVE-2018-12886

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


                            1. Antervis
                              20.09.2019 14:37

                              «разными версиями языка», а не «разными версиями компилятора».


                              1. KanuTaH
                                20.09.2019 14:39

                                Так такого в растике как раз и нет. Выше вон предлагают разными версиями компилятора собирать. А если именно новые версии компилятора поддерживают старые версии языка (мейнстримные C компиляторы вон даже K&R поддерживают), то это и есть забота об обратной совместимости.


                                1. mayorovp
                                  20.09.2019 14:50

                                  Но ведь именно так и есть, новые версии rustc поддерживают старые версии языка, за что отвечает опция командной строки --edition


                                  1. KanuTaH
                                    20.09.2019 14:52

                                    И вышеупомянутые deprecation warnings (которые со временем превратятся в errors) это починит?


                                    1. mayorovp
                                      20.09.2019 14:58

                                      Я очень сильно удивлюсь если нет.


                                      1. KanuTaH
                                        20.09.2019 15:05

                                        Просто в C я могу свободно написать например такое:

                                        int bar(b)
                                        int b; // K&R syntax
                                        {
                                            return b;
                                        }
                                        
                                        int foo(a)
                                        int a; // K&R syntax
                                        {
                                            int x = 100;
                                        
                                            bar(x);
                                        
                                            int y = 200; // C99 feature
                                        
                                            return y;
                                        }
                                        
                                        int main()
                                        {
                                            return foo(1);
                                        }
                                        

                                        и все прекрасно соберется без единого предупреждения в -std=c99 -pedantic и с закономерным предупреждением про «ISO C90 forbids mixed declarations and code» в -std=c89 -pedantic (но все равно соберется). В Расте, я так понимаю, все же либо одно, либо другое — либо условный K&R, либо условный C99?


                                        1. vitvakatu
                                          20.09.2019 15:51

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


                    1. 0xd34df00d
                      20.09.2019 16:18
                      +1

                      В «мире C++» так заботятся об обратной совместимости именно потому, что тут более реальные представления о стоимости правок старого кода.

                      Именно поэтому я прямо сейчас сижу и думаю, что мне делать с вендорской либой, которая имеет throw-спецификации в хедерах, но клиентский код хочется собирать свежими C++17-компиляторами, права редактировать хедеры у меня нет по юридическим причинам, а делать новую версию вендор не хочет/не может.


                      1. KanuTaH
                        20.09.2019 16:21

                        Сделать перед их включением #define, который обнулит throw, а после сделать ему #undef? Ну в целом да, со throw «нехорошо получилось». К счастью, это скорее исключение, чем правило.


                        1. 0xd34df00d
                          20.09.2019 16:22

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


                          1. KanuTaH
                            20.09.2019 16:42

                            У clang помогает сборка с "-std=c++17 -Wno-dynamic-exception-spec" (да, в c++17 это уже не warning, а error, но указание этого ключика все равно прокатывает). В gcc наверное тоже есть что-нибудь вроде этого.


                            1. 0xd34df00d
                              20.09.2019 16:45

                              Но это уже не стандартный C++ :)


                    1. mkpankov
                      20.09.2019 19:26

                      Нет, это практика того, как это реально происходило в мире Rust за время существования стабильности.
                      Могу припомнить одну серьезную проблему — что-то там сделали то ли с ptr::copy, то ли с крейтом libc.
                      Сейчас вот намечается ещё одна волна потенциально серьёзных правок — когда старый unsound код принимался старым borrow checker'ом. Это не на пару минут.


            1. mkpankov
              20.09.2019 14:06

              Вы то ли специально передёргиваете, то ли в каком-то своём мире живёте.
              Ну хотите использовать старый компилятор — используйте старый.
              Хотите обновлять свой энтерпрайз когда надо — обновляйте когда надо.
              В 2014 году библиотек на стабильном Rust не было — он вышел в 2015.
              Бегать и фиксить депрекейшены за 4 года мне не приходилось ни разу — и зачем сразу бегать, если это warning? Если вы такие все консервативные, можете сидеть хоть на 1.0 до сих пор.
              И у вас прекрасно продолжат компилироваться старые версии, даже если новым зачем-то понадобился более новый компилятор. В приложениях версии прибиты файлом с версиями.


              1. amarao
                20.09.2019 14:13

                Я не передёргиваю, я объясняю, почему Rust трудно попасть в энтерпрайз. Энтерпрайз не хочет переписывать что-либо. Он хочет делать новое и чтобы старое работало. Это означает, что в 2019 мы пишем библиотеку, и если нам не нужно туда новых фич, мы её не трогаем следующие 20 лет. Для Си это прокатывает — можно иметь сишную библиотеку, которая была написана в 1999 году, которую можно скомпилировать в 2019 году. (ANSI C, да). При этом мы постоянно хотим новые штуки в тот момент, когда захотелось, но не ценой переписывания старых.


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


                Вот когда Rust сможет пообещать не ломать ничего на годы (десятилетия) вперёд, вот тогда он станет enterprise ready.


                1. PsyHaSTe
                  21.09.2019 09:53

                  Вот когда Rust сможет пообещать не ломать ничего на годы (десятилетия) вперёд, вот тогда он станет enterprise ready.

                  Так он ничего и не ломает. Компилируйте с флагом --2015 и он будет компилировать как и в 2015 году. Последней версии компилятора. В чем проблема-то?


                  1. MikailBag
                    21.09.2019 23:23

                    Ну, проблема в том что вроде как в течение 3-4 месяцев AST borrowck должны полностью выпилить, и какой-то код все-таки сломается.


      1. merhalak
        19.09.2019 10:23

        По моему, ввод новых ребят все же будет дешевле: Rust не позволит выполнять простейшие ошибки и возьмёт на себя часть Code Review по части некорректной работы с временем жизни переменных. Да и сами времена жизни будут неплохой подсказкой по логике работы во время ввода в проект.


        Останется лишь проверять логику решения.


        Т.е. хорошо подойдет для больших и долгоживущих проектов. Маленькие лучше будет писать на Go или Python, если важен быстрый старт.


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


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


        1. JekaMas
          19.09.2019 10:52

          Моё мнение пока противоположное: лучше применять на уровне отдельного небольшого сервиса или библиотеки, где критична скорость и безопасность, и не переплачивать за ненужную в других частях проекта строгость и дорогую поддержку.


          1. merhalak
            19.09.2019 11:05
            +1

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


            Но строгость всегда была положительным моментом для самих программистов — меньше времени писать тесты (да, бывает, что тесты пишут все же), ибо строгость языка часть проблем покрывает. Меньше нервов по поводу того, что: поменяю вот здесь, что сломаю? Особенно — для новичков в проекте.


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


            1. JekaMas
              19.09.2019 11:44

              И? С утверждением "каждой задаче свой инструмент" вы же не спорите?


              1. merhalak
                19.09.2019 11:50

                Ага. Маленьким, требующим скорость разработки, непродолжительным проектам — динамика, Python и аналоги.


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


                1. JekaMas
                  19.09.2019 11:55

                  Тогда почему не Haskell? Народ нормально в проде использует.


                  Но повторюсь, странно слышать, что любой задаче одно решение.


                  1. 0xd34df00d
                    19.09.2019 16:28
                    +1

                    Тогда почему не Haskell? Народ нормально в проде использует.

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


                1. JekaMas
                  19.09.2019 12:04

                  А можете чуть подробнее о "видел достаточно" рассказать? Что за проекты, какие команды, почему пришли к тому, что rust именно для больших проектов? Очень любопытно! Если есть основания для подобного утверждения и цифры показали меньшую цену поддержки, то rust можно было бы сделать долгосрочным проектом на проде, учитывая время на обучение спецов.


                  1. merhalak
                    19.09.2019 12:31

                    Я не говорю о том, что вводить надо сейчас и именно Rust. Я о том, что его строгость в большом — не излишняя. И то что привносит в Rust тоже со временем пригодится.


                    Понятное дело, сейчас Rust в прод не потащишь. Ни спецов, ни специальных библиотек. В мое случае даже наша платформа (Java) используется неполностью, ибо никто не хочет писать аннотации для FindBugs или аналогов. "Код загромождает". А потом носимся с багами, когда подавили NPE, а где подавили — хз.


                    Я как раз горю с того, что в больших проектах забивают даже на это.


                    1. JekaMas
                      19.09.2019 12:38

                      То есть вывод основан на опыте в одном конкретном проекте, где применяют не лучшие практики?


                      1. merhalak
                        19.09.2019 12:51

                        3 проекта из 4-х. Практически подряд. Не, у меня всё ещё начало карьеры (порядка 2.5 лет опыта работы), но тенденция пугает.

                        Может быть я просто свинья, которая везде грязь найдет.

                        Но пока что я вляпывался: очень низкое покрытие тестами (причем покрываются только конкретные реализации), слабое использование (фактически нет) статического анализа, собственные велосипеды, где документацию заменяют тесты. И да, тесты больше как «usage examples» нежели как тесты.

                        Хочу больше перфекционизма. В новом проекте у меня наконец то появилась возможность что-то поменять в подходах. Вопрос в том, что нормального опыта у меня маловато, за пару лет у меня code review был только 1 или 2 раза. Так что всё на собственных ошибках.

                        Так что я хочу сначала использовать всё, что предоставляет любимая жабка, а потом смотреть, готова ли инфраструктура Rust (например сейчас самая необходимая и «толстая» зависимость — Hazelcast или его аналог). Будет готова — буду агитировать за. Не будет готова — ну, спасибо Oracle и сообществу, жабка тоже на месте не стоит.


                        1. JekaMas
                          19.09.2019 14:05

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


                          1. merhalak
                            19.09.2019 14:22

                            Уточни вопрос, пожалуйста.


                1. 0xd34df00d
                  19.09.2019 16:30
                  +1

                  Маленьким, требующим скорость разработки, непродолжительным проектам — динамика, Python и аналоги.

                  Это совсем не по теме треда, но я вот очень часто вижу упоминание Python в контексте таких проектов, и не могу понять, почему. Я на нём ради интереса не далее чем неделю назад попытался что-то наваять (что-то очень тупое, логи распарсить и очень простую статистику по сообщениям подсчитать), и, ну, в общем, у меня не получилось, я очень быстро утонул.


                  1. Gymmasssorla Автор
                    19.09.2019 16:52

                    Python всё-же хорош для простых проектов и прототипирования, т.к. позволяет программисту не думать о правильном построении архитектуры.


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


                    1. PsyHaSTe
                      19.09.2019 17:02
                      +3

                      Может я глупый, но я на питоне не могу ничего написать. Именно по этой причине.


                    1. 0xd34df00d
                      19.09.2019 17:08

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


      1. Revertis
        19.09.2019 11:48

        Да, скорость разработки на Rust страдает. Но не стоит забывать, что в сроки разработки добавляется срок выявления и фикса багов, который в программе на C/C++ вы будете фиксить годами.


        1. JekaMas
          19.09.2019 11:58

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


          1. Revertis
            19.09.2019 12:03

            А кто может достоверно определить критичность сервиса или библиотеки?
            Если вдруг ваш скрипт, зависящий от «некритичной» библиотеки, на две минуты позже известит вас об освобождении туалета, это будет критичным или нет? ;)


            1. Cerberuser
              19.09.2019 12:42

              Всё зависит от того, успеют ли за эти две минуты его занять снова...


              1. Revertis
                19.09.2019 12:50

                Вот и я о том же. И некая безделушка может оказаться критичной.


            1. PsyHaSTe
              19.09.2019 13:28

              Обычно пишется прототип. Если выигрыш от него ощутим то им заменяют основной продукт.


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


              1. 0xd34df00d
                19.09.2019 16:31

                О, а чего у вас за проект такой интересный?


                1. PsyHaSTe
                  19.09.2019 16:57

                  Искать маршруты на графе.


            1. JekaMas
              19.09.2019 13:28

              Если так идти, то зачем мне полумеры в лице Rust? Почему не брать что-то с ещё более мощной системой типов?


              1. PsyHaSTe
                19.09.2019 13:29

                Ничего более производительного и одновременно с более мощной системой типов не придумали.


                1. 0xd34df00d
                  19.09.2019 16:35
                  +1

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


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


                  1. PsyHaSTe
                    19.09.2019 17:04

                    Согласен, но если честно меня немного смущает строгость его тайпчекера. Морально уничтожить интервьювера разворотом списка на идрисе это всегда прикольно, но написать сколько-нибудь крупный проект… Плюс "для 1.5 человек неплхо", я согласен, но для решения задачи обычно смотрят на абсолютное качество инструмента, а не на удельное на количество человеколет. Надеюсь увидеть завтипы в распространенных языках, или идрис в массы (да, я наивный), но в 2019 году это пока просто игрушка.


                    1. 0xd34df00d
                      19.09.2019 17:13
                      +1

                      Ну, вас же никто не заставляет этой всей строгостью пользоваться. Не пишете %default total — и вам больше не нужно доказывать тотальность функций (или писать мозолящее partial у каждой функции, где это делать лень). Пишете euclid : (m, n : Nat) -> Nat вместо euclid : (m, n : Nat) -> Either (0 `LT` m) (0 `LT` n) -> (d ** Gcd d m n) — тоже ничего доказывать не надо. Получается этакий строгий по умолчанию хаскель.


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


    1. mkpankov
      20.09.2019 08:59

      Ошибки аллокации достаточно бесполезно ловить в пользовательской программе.
      Во-первых, почти вся стандартная библиотека опирается на аллокатор, но если добавить тип ошибки в возвращаемые значения всех функций, которые выделяют память, пользоваться этим будет совершенно невозможно.
      Во-вторых, как минимум на десктопных Linux по умолчанию включён overcommit, который делает невозможной локальную обработку ошибки выделения памяти.
      В-третьих, если вы пишете прикладную программу, близкую к системной в плане управления ресурсами (веб-сервер, сервер БД), то у вас и так весьма особенные нужды в выделении памяти. Часто используются пулы, маппинг файлов/анонимной памяти прямо от системы. Куча в первую очередь интересна "совсем прикладным" программам, которым нехватка ресурсов неинтересна.


      1. 0xd34df00d
        20.09.2019 16:21

        Собственно, даже в C++ всерьёз подумывают (были треды в мейллисте комитета) разделить выделение памяти на два класса — для «обычных объектов» типа строк, ошибки выделения памяти для которых никто не обрабатывает, и для больших объектов, типа каких-нибудь там матриц.


  1. Strawb
    18.09.2019 16:41
    +3

    Картинка доставила.
    На следующем кадре мужик бъет палкой по лицу это чудовище и делает ноги.


    1. MikailBag
      18.09.2019 16:47
      +2

      Но далеко не убегает, потому что ноги прострелены.


      1. Strawb
        18.09.2019 16:50
        +1

        Ну вот таких если с простреленными ногами чудовище и пожирает.


  1. red75prim
    18.09.2019 16:54
    +6

    не умеет обрабатывать ошибки аллокации. Пока эту проблему не решат, он не может быть настоящим системным языком.

    Хм, мне всегда казалось, что язык системный, если, в частности, на нём можно этот самый аллокатор написать (на Rust это можно сделать). std-часть стандартной библиотеки, которая использует существующий аллокатор и не позволяет обработать OOM, больше относится к прикладному программированию.


  1. dkfrmmnt
    18.09.2019 17:10

    Не понял, что такое «несовместимые между собой синтаксические конструкции». Можно увидеть примеры?


    1. MikailBag
      18.09.2019 17:47
      +4

      Ну например:


      1. auto x = baz();
        vs
        decltype(auto) x = baz();


      2. Многообразие синтаксических форм для инициализации
        std::vector foo (s.begin(), s.end());
        vs
        std::vector foo {s.begin(), s.end()};
        Более того, НЯП в одной строке создается вектор интов на основе двух итераторов, а в другой вектор итераторов.



      1. Gymmasssorla Автор
        18.09.2019 17:53

        Ещё добавить в этот список несовместимость синтаксисов Си и Си++. Например, разное значение слова auto, функция с пустыми скобками и т.д., смотреть https://mcla.ug/blog/cpp-is-not-a-superset-of-c.html.


    1. PsyHaSTe
      18.09.2019 17:58
      +11

      image


  1. GrimMaple
    18.09.2019 17:24
    +1

    На данный момент Rust — единственный язык программирования, обладающий одновременно активным сообществом и характеристиками, позволяющими ему решать задачи, решаемые языками C/C++.

    А как же D? :'(


    1. PsyHaSTe
      18.09.2019 17:59
      +6

      активным сообществом


      1. GrimMaple
        18.09.2019 18:04
        +6

        Пожалуйста, не сыпьте соль на рану…


  1. lega
    18.09.2019 17:39
    +1

    Также можно отметить библиотеку Rustls, обогнавшую знаменитую OpenSSL практически во всех тестах
    А вы уверены, что более быстрая скорость достингута именно из-за Rust, а не (например) более оптимальным алгоритмом?


    1. Gymmasssorla Автор
      18.09.2019 17:50
      +5

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


      Ещё может играть роль криптографическая библиотека ring, используемая в Rustls и частично написанная на Ассемблере.


      1. lega
        20.09.2019 12:44
        -3

        С учетом того, что раст использует llvm и «unsafe» для оптимизации — говорит о том, что сам раст не может быть быстрее чем С++, который тоже может быть собран с llvm и он всегда «unsafe». А если учесть то что llvm часто дает менее производительный код, стоит говорить о том на сколько раст медленее чем c/c++.
        Впрочем по скорости он близок и более важный вопрос — какие фичи он дает за это все, и стоит ли оно того.


      1. KanuTaH
        20.09.2019 22:40
        +2

        Криптография — это очень сложная штука, и мерять производительность «в лоб» там — сомнительная практика. Например, есть такой класс атак, как timing attacks, когда некие представления о сути зашифрованных данных получаются атакующим исходя из времени, которое атакуемый тратит на их шифрование или дешифрование. Для борьбы с атаками такого рода алгоритмы специально реализуются так, чтобы время выполнения их на любых данных было константным (обычно это означает «наихудшим из возможных»). Так что я бы не стал сильно радоваться тому, что rustls «быстрее». Мало ли почему она быстрее — может, как раз потому, что делает всякие early exits тогда, когда этого бы не стоило делать.


    1. mayorovp
      18.09.2019 17:57
      +4

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


      Общеизвестно, что самые быстрые программы пишутся на ассемблере. Вот только "почему-то" OpenSSL так никто на ассемблер не переписал.


      1. PsyHaSTe
        18.09.2019 18:00
        +3

        Общеизвестно, что самые быстрые программы пишутся на ассемблере.

        Citation needed


        1. mayorovp
          18.09.2019 18:06

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


          Но логика тут простая: любую программу можно преобразовать в ассемблерный листинг, следовательно ассемблер как минимум не хуже любого другого языка. А поскольку одинаковых языков не бывает, он — самый лучший! :-)


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


          1. PsyHaSTe
            18.09.2019 18:33
            +1

            Ну это "можно" из разряда "boggle sort это тоже алгоритм сортировки". Разве что чисто формально.


          1. vitvakatu
            18.09.2019 21:22
            +5

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


            1. Groramar
              18.09.2019 21:37

              Да всё там пишется и с векторизацией, с учетом кэшей, конвееров и так всяко. Сложно и с нюансами, бесспорно. И мало кому нужно. Но если нужно — то пишется.


            1. mayorovp
              18.09.2019 22:30

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


          1. mkpankov
            20.09.2019 09:28
            -1

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


            1. mayorovp
              20.09.2019 09:46

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


            1. 0xd34df00d
              20.09.2019 16:25

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


        1. Groramar
          18.09.2019 21:32

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


          1. PsyHaSTe
            18.09.2019 22:00

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


      1. lega
        18.09.2019 18:55

        Причём тут важны не только дешевизна абстракций, но и объём помощи со стороны компилятора.
        Т.е. если я правильно понял, вы говорите, что Rust быстрее чем C++ потому что в С++ используют горы обвязок из умных указателей и прочего, а в Rust-е нет.? Справедливаое ли сравниение?, а как же C, там вроде с обвязками попроще?

        Общеизвестно, что самые быстрые программы пишутся на ассемблере.
        Да, но Rust не на столько более высокоуровневый чем C++, да и вообще более ли?


        1. mayorovp
          18.09.2019 19:16

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

          Нет, я не утверждал ничего из этого, это вы додумали.


          Да, но Rust не на столько более высокоуровневый чем C++, да и вообще более ли?

          И этого я тоже не утверждал.


        1. vitvakatu
          18.09.2019 21:25
          +2

          Да, но Rust не на столько более высокоуровневый чем C++, да и вообще более ли?

          В среднем более высокоуровневый, на уровне последних стандартов С++ (даже тех, которые еще только на этапе проработки).


          Это не значит, что Rust быстрее.
          Это не значит, что C++ быстрее.
          Это не значит, что в Rust нельзя писать низкоуровневый код.


        1. PsyHaSTe
          18.09.2019 22:01

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

          Это один из примеров. Второй — в расте можно безопасно шарить данные, Которые в С++ разделять побоятся. Хорошая статья на тему.


  1. tangro
    19.09.2019 00:06
    +2

    Rust, я так думаю, займёт своё место в наиболее критических местах: прошивки автомобилей, софт для самолётов, код управления важными техпроцессами. Там, где можно позволить себе потратить лишнюю копейку на программиста, но получить в итоге и быструю программу и гарантию того, что она не навернётся на buffer overflow или dead lock.

    Обычный код на С++ вытеснен Rust'ом не будет. А зачем?


    1. KanuTaH
      19.09.2019 00:36
      +1

      Интересно, и как же Rust сам по себе гарантирует отсутствие deadlocks? Утрированный пример: есть два потока, один захватывает мутекс и осуществляет блокирующую запись, скажем, в пайп или сокет, второй должен захватывать этот же мутекс и читать из того же пайпа или сокета "с другой стороны". Первый захватил мутекс, начал писать в пайп и встал потому, что буфер у пайпа закончился, второй должен бы по-хорошему прочитать из пайпа и освободить немного буфера, но не может потому, что не может захватить мутекс — вот тебе и deadlock.


      Тем, кто считает, что это пример искусственный, и "кто так пишет", рекомендую взглянуть на этот issue:


      https://github.com/servo/servo/issues/22921


      1. red75prim
        19.09.2019 00:51
        +2

        tangro ошибается. Rust предотвращает data races, а не dead locks.


        1. tangro
          19.09.2019 10:29

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


        1. tangro
          19.09.2019 13:02
          +1

          Вот, например, Firefox анализировал баги в своём движке (а точнее в его части, ответственной за парсинг CSS): hacks.mozilla.org/2019/02/rewriting-a-browser-component-in-rust

          Насчитали 70 багов, из них 43 связанных с секьюрити, из них в 32 случаях Rust не дал бы даже скомпилироваться коду, который привёл к ошибке. Это много.


          1. KanuTaH
            19.09.2019 13:10

            Знаете, языков, которые тем или иным образом предотвращают то, с чем пытается бороться Rust, а именно попытками записи/чтения туда/оттуда, откуда это делать нельзя, уже давно полно (любой managed язык — Java, C#, JS, прости господи, PHP), а багов, связанных с секьюрити, меньше не становится, а, я бы сказал, становится даже больше — особенно на веб-сайтах, на которых вообще никто не задумывается о ссылках, указателях и прочих низкоуровневых деталях в силу специфики средств разработки. Разруха — она не в клозетах, а в головах.


            1. mayorovp
              19.09.2019 13:29

              Много ли RCE найдено в сайтах на ASP.NET?


              1. KanuTaH
                19.09.2019 13:33

                1. mayorovp
                  19.09.2019 13:40
                  +1

                  Ну хорошо, одна есть, хоть авторам кода и пришлось постараться чтобы такое допустить (я вот не могу даже представить зачем мне бы понадобилось динамически формировать путь к компоненту на основе ввода пользователя). А ещё?


                  1. KanuTaH
                    19.09.2019 13:44
                    +1

                    Мои услуги по гуглению платные :) Согласитесь, что ошибки типа «можно залить и выполнить файлик» или «можно обойти аутентификацию через SQL injection, войти под админом, залить и выполнить файлик», и так далее, и тому подобное — отнюдь не редки.


                    1. PsyHaSTe
                      19.09.2019 13:45

                      Это видимо один из 11 случаев, когда раст дал бы программе скомпилироваться. Все равно хорошо.


                      1. KanuTaH
                        19.09.2019 13:47

                        Почему именно из 11? :) Довольно произвольная цифра :)


                        1. PsyHaSTe
                          19.09.2019 13:47

                          43-32


                          1. KanuTaH
                            19.09.2019 13:54

                            Это только из того, что было обнаружено конкретно данным анализом, причем анализом на баги конкретного типа — обращение к неинициализированным переменным, выход за границы массива, и проч. Баг, подобный «можно залить и выполнить файлик» вряд ли вообще был бы обнаружен в ходе анализа подобного рода.


                    1. mayorovp
                      19.09.2019 14:02

                      Не редки, но от них очень просто защититься, нужно лишь немножечко внимательности. А более новый ASP.NET Core так и вовсе по умолчанию от этого класса ошибок полностью защищён.


                      Для сравнения, от переполнения буфера или там use after free в С++ без проседания производительности защититься куда труднее.


            1. PsyHaSTe
              19.09.2019 13:31
              +1

              Вот я за 6 лет продакшн разработки на сишарпе ни разу не видел выходы за границы буфера или поверждение памяти. Худшее, что может случится — NullRefException или KeyNotFoundException при неаккуратном поиске по словарю. Поэтому "да в ваших манагед языках проблем не меньше" — на порядки меньше проблем с ними.


              1. KanuTaH
                19.09.2019 13:34

                Remote Code Execution, вопреки мнению нашего коллеги, от этого, однако, никуда не делись. Я выше привел ссылку на одну такую от этого года — банально из гугла.


                1. PsyHaSTe
                  19.09.2019 13:42

                  RCE ортогонален проблемам с памятью, если что.


                  1. KanuTaH
                    19.09.2019 13:45

                    Угу. Только мне тут попытались доказать, что RCE в отсутствие проблем с памятью не случаются, а это не так.


                    1. PsyHaSTe
                      19.09.2019 13:47

                      Не знаю, зачем про RCE вообще вспомнили. Я говорил вполне четко — коррапт памяти, хартблиды и вот это все. На managed-языках такого не происходит, если люди только не отказываются от встроенного механизма работы с памятью (бывают оригиналы, которые выделяют гигабайтный массив в начале программы и начинают с ним работать). Но что поделать, от сумы не зарекайся.


                      1. KanuTaH
                        19.09.2019 13:49

                        Да, но коррапт памяти и хартблиды — это средство, а конечная цель — хищение и/или повреждение данных, а также получение удаленного контроля. И я бы не сказал, что проблем с этим стало как-то кардинально меньше после появления managed языков.


                        1. PsyHaSTe
                          19.09.2019 13:56

                          Мое мнение, что стало.


                          1. KanuTaH
                            19.09.2019 13:57
                            +1

                            Ну, значит, будем констатировать несовпадение во мнениях :)


                            1. PsyHaSTe
                              19.09.2019 14:02

                              Да


            1. red75prim
              19.09.2019 13:39
              +2

              А что мешает и разрухой в головах заниматься и унитаз с гидрозатвором вместо дырки в полу поставить?


            1. tangro
              19.09.2019 18:39

              Странно говорить, что Java, C#, JS или, прости господи, PHP не решили никаких проблем, учитывая популярность и распространённость этих языков в современном мире. Они полностью исключили целый ряд проблем и типичных ошибок. Просто само наличие в них Garbage Collector делает их неприменимыми в некоторых классах задач, а Rust пытается решить те же проблемы, но без Garbage Collector. Поэтому я не вижу, почему бы ему в этом не преуспеть.


              1. Gymmasssorla Автор
                19.09.2019 19:25

                Rust не пытается решить те же проблемы, что решают Java/C#/PHP, он скорее разрабатывался для систем с быстрым откликом (высоконагруженные сервера, прошивки автомобилей). Всё-таки на C# программировать бизнес-логику куда проще, чем на Rust, т.к. не приходиться думать о всяких RefCell/Cell/UnsafeCell/etc.


            1. mkpankov
              20.09.2019 09:32

              И ни один из этих языков не пересекается по нише с Rust.
              Сравнивать rustls с openssl — нормально. Когда первая достигнет объёмов использования хотя бы в 10% от openssl, можно будет увидеть, что уязвимостей там правда меньше.
              При этом не стоит забывать, что поиск уязвимостей тоже не стоит на месте. Благодаря ASLR, защите стека, DEP и другим прелестям, классическое переполнение буфера уже когда сложнее превратить в эксплуатируемую уязвимость — а значит внимание смещается на "менее технические" моменты, не связанные с конкретным языком программирования. Социальная инженерия, например, сильна как никогда.


    1. Gymmasssorla Автор
      19.09.2019 00:46

      Переписывать код с C++ на Rust затруднительно (переписывание с любого языка затруднительно), и, скорее всего, проекты, написанные на C++ на нём и останутся.


      Писать новые проекты на Rust имеет смысл из-за более быстрой скорости разработки, т.к. меньше бойлерплейт-кода, библиотеки в целом эргономичнее, нет возни с системами сборки и поиском UB в коде.


      1. Antervis
        19.09.2019 19:45

        Писать новые проекты на Rust имеет смысл из-за более быстрой скорости разработки, т.к. меньше бойлерплейт-кода

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


        1. Gymmasssorla Автор
          19.09.2019 19:53

          Можете привести пример? Композиция в большинстве случаев работает хорошо, если она вообще нужна.


          1. a1ien_n3t
            19.09.2019 22:27

            Отличный пример например реализация парсера xml'е подобных сущностей.
            Где есть бызовый элемент. И куча его спецификацей.
            В расте это решается либо копипастой релизацией трейта. Либо нетривиальными макросами либо еще чемто. Тоесть да решить или написать это можно но требуется гораздо больше телодвижений.


            1. PsyHaSTe
              19.09.2019 22:55
              +1

              Не вижу сложностей. Каждый узел дерева это просто энум из нескольких возможных значений. И мы с ними что-то делаем.


              Вы что-то путаете. Наоборот, для обхода XML исторически используют визиторы, которые ни что иное как способ эмулировать ADT в ООП языках :)


              1. technic93
                19.09.2019 23:41

                Но почему то в serde там какой то условный бойлер-плейт из функций visit_abc вместо enum. Я пытался обернуть их апи на матч по енуму, но не получилось. И мне помнится проблема была в том что там навёрнут ещё полиморфизм с точки зрения разных парсеров (хмл жсон и т п).


                Т.е. все visit_abc<xyz: parser> так устроено.


                1. vitvakatu
                  20.09.2019 09:50
                  +1

                  Да, потому что serde — это не парсер, а инструмент для создания парсера (вернее, сериализатора и десериализатора). Enum'ы там применять нельзя, потому что у каждого формата свой Enum, а на этапе компиляции заменять их не получится. Вот и пришлось прикручивать Visitor. Но вот реализация самих сериализаторов, как например serde_json — уже основана на использовании enum'ов.


                  1. technic93
                    20.09.2019 10:51

                    Парсер т.е. десиарелизатор джсона там тоже на этих визиторах. Да он может построить дерево из енумов на выходе, но это не всегда хороший вариант т.к. связано с аллокацией памяти для словаря и списка. А хочется прозрачный парсер на енумах. Т.е. чтобы вместо того чтобы переопределять visit_str, visit_vec и т п (а там из за дженериков эти определения длиной во всю строку) для каждой структуры, я мог сделать аналогичный match, который заявляется как одна из киллер фич раста.


          1. Antervis
            20.09.2019 11:37
            +1

            TristateCheckBox -> CheckBox -> Button -> Widget. У Widget'а есть метод show(), но его придется явно реализовывать для Button, CheckBox, и TristateCheckBox


            1. vitvakatu
              20.09.2019 11:42

              Ну вообще-то в вашем примере у всех этих классов разная реализация show должна быть и наследование тут никак не поможет. Могли бы привести в пример getBounds, например.


              1. Antervis
                20.09.2019 11:55
                +1

                Ну вообще-то в вашем примере у всех этих классов разная реализация show должна быть

                inline void Widget::show() {
                    set_visible(true);
                }

                вместо
                // Widget:
                fn show(&self) {
                    self.set_visible(true);
                }
                
                // Button:
                fn show(&self) {
                    self.widget.show();
                }
                
                // CheckBox:
                fn show(&self) {
                    self.button.show();
                }
                
                // TristateCheckBox:
                fn show(&self) {
                    self.checkbox.show();
                }


                1. vitvakatu
                  20.09.2019 12:32

                  А, пардон, я думал что show() отрисовывает виджет на экране непосредственно.
                  Чтобы скомпенсировать свою оплошность, привожу пример как это может выглядеть на расте:


                  pub trait Show: Visible {
                       fn show(&self) {
                            self.set_visible(true);
                       }
                  }
                  
                  impl Show for Button {}
                  impl Show for CheckBox {}
                  impl Show for TristateCheckBox {}

                  Но на практике конечно так весело редко получится.


                  1. mayorovp
                    20.09.2019 12:38

                    Так тут поведение отличается...


                  1. Antervis
                    20.09.2019 12:59
                    +1

                    ну отлично. Вы написали 8 строк для абстрактного show(), но теперь нам для каждого из этих классов по-разному реализовывать set_visible(). Усугубили проблему, так сказать


                    1. vitvakatu
                      20.09.2019 13:20

                      Почему же усугубил? Хитро переложил количество кода с show на set_visible, но усугубления тут нет, общее количество кода в общем то такое же.


                      1. Antervis
                        20.09.2019 13:42
                        +3

                        Именно что усугубили. Если в моём примере надо было один раз написать Widget::set_visible() и несколько версий show(), то в вашем получается 8 строк для трейта show(), но для каждого из классов придется писать свой set_visible() чтобы реализовать трейт Visible, который объявляется еще несколькими строками. Ваш подход наверно соответствует «rust way» и подходит для большинства случаев, но не помогает бороться с бойлерплейтом в конкретном.

                        По сути, у всех классов из примера должны отличаться:
                        а. методы paint(),
                        б. обработчик on_clicked()
                        в. свойства checkable/checked для Button и check_state для CheckBox/TristateCheckBox.

                        При этом они будут делить между собой очень много методов Widget'а, логика которых отличаться не должна. Те самые свойства visible/width/height/style/pallette/parent/children/… и их алиасы show()/hide()/geometry()/… В расте (как минимум пока что) нет удобного способа переиспользования такого вот кода без бойлерплейта. Кстати, в т.ч. поэтому переписывать с плюсов на раст столь болезненно.


                        1. vitvakatu
                          20.09.2019 15:54

                          Да, признаю свою ошибку. Что поделаешь, в Расте действительно нет наследования и действительно с плюсов код не переписать. Расстраивает тут разве что то, что без наследования никто не умеет рисовать интерфейсы нормально (впрочем, с наследованием нормально умеет только C++, похоже).


                          1. mayorovp
                            20.09.2019 16:37

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

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


                            1. vitvakatu
                              20.09.2019 16:40

                              Да, в терминах существующих GUI библиотек это так. Но что значит "обсуловлено предметной областью"? Вы же не станете утверждать, что "являтся виджетом", это неотчуждаемое свойство всех элементов графического интерфейса? Это абстракция, обсуловленная популярными GUI библиотеками, основанными на наследовании, не более.


                            1. math_coder
                              20.09.2019 16:59

                              А зачем делать кнопку структурой? Пусть и кнопка, и виджет будут трейтами. У трейтов наследование есть.


                              1. mayorovp
                                20.09.2019 17:04

                                Затем, что у неё свои поля есть.


                                1. 0xd34df00d
                                  20.09.2019 17:21

                                  А в чём проблема с таким подходом (пардон за синтаксис, я растовский не знаю)?


                                  class Widget w where
                                    show :: w -> IO ()
                                    hide :: w -> IO ()
                                    paint :: w -> IO ()
                                  
                                  class Widget l => TextInput l where
                                    setValue :: String -> l -> IO ()
                                    getValue :: l -> IO String

                                  Я гуи никогда на функциональщине не делал, поэтому не знаю, как это делать правильно, но сходу в таком подходе никаких проблем не вижу.


                                  Ну и можно поиграться даже с более широкой иерархией классов, когда у вас будет


                                  class Widget w where ...
                                  class HasText ht where
                                    setValue :: String -> l -> IO ()
                                    getValue :: l -> IO String
                                  
                                  class (Widget tl, HastText tl) => TextLine tl
                                  class (Widget mte, HasText mte) => MultilineTextEdit mte

                                  В ООП это начинает либо плохо пахнуть со множественным наследованием, либо начинается эмуляция трейтов на интерфейсах.


                                  Надо бы ещё куда-нибудь multiparam type classes запихнуть...


                                  1. mayorovp
                                    20.09.2019 17:28

                                    Это вы только API определили. А теперь напишите реализацию этой радости.


                                    1. 0xd34df00d
                                      20.09.2019 17:37

                                      -- где-то в библиотеке
                                      {-# LANGUAGE DataKinds, PolyKinds #-}
                                      
                                      data WindowType = TextLine | MultilineTextEdit
                                      
                                      -- за счёт PolyKinds пользователь библиотеки
                                      -- может объявлять свои типы окон и доопределять
                                      -- нужные классы
                                      newtype NativeWindow (wty :: k) = NativeWindow { wid :: Int }
                                      
                                      class Creatable (wty :: k) where
                                        createWid :: Proxy wty -> IO Int
                                      
                                      instance Creatable TextLine where
                                        createWid _ = underlyingCLibraryCreateTextLine
                                      
                                      instance Creatable MultilineTextEdit where
                                        createWid _ = underlyingCLibraryCreateMTE
                                      
                                      instance Creatable wty => Widget (NativeWindow wty) where
                                        show = underlyingCLibraryShow . wid
                                        hide = underlyingCLibraryHide . wid
                                        paint = genericPaint . wid
                                        -- забыл в том интерфейсе метод create :: IO w
                                        create = NativeWindow <$> createWid (Proxy :: Proxy wty)
                                      
                                      instance HasText (NativeWindow TextLine) where
                                        setValue str = cLibrarySetTextLineStr str . wid
                                        ...

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


                                      Можно ещё там обмазаться семействами типов, чтобы create :: CreateParams w -> IO w, например, дописать type role для тега у NativeWindow (чтобы компилятор не выводил Coercible), короче, есть, как развернуться ещё.


                                      1. mayorovp
                                        20.09.2019 17:40

                                        Вот это неинтересно:


                                            setValue str = cLibrarySetTextLineStr str . wid

                                        Такой подход для биндингов хорош, а интересует полноценная реализация своих контролов.


                                        1. 0xd34df00d
                                          20.09.2019 17:41

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


                                          1. mayorovp
                                            20.09.2019 17:47

                                            Да не получится аналогично!


                                            Вот есть у меня какой-нибудь


                                            data MyControl = MyControl {
                                                 parent :: NativeWindow TextLine,
                                                 state :: IORef Int state
                                            }

                                            Как мне делегировать show/hide/setValue/getValue (и ещё 100 разных функций) parent, а paint определить самому?


                                            1. 0xd34df00d
                                              20.09.2019 18:04

                                              Так — никак. У меня там весь расчёт на то, что можно


                                              data MyControlTag = MyControlTag
                                              
                                              netype MyControl = NativeWindow MyControlTag

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


                                              Если вам действительно охота таскать с собой свои данные, то можно вместо newtype сделать NativeWindow типом данных в стиле trees that grow:


                                              type family WindowData (tag :: k)
                                              
                                              data NativeWindow tag = NativeWindow
                                                { wid :: Int
                                                , wdata :: WindowData tag
                                                }

                                              Правда, тогда его лучше назвать GenericWidget или как-то так.


                                              Норм, хорошее обсуждение, ща гуи-фреймворк так напишем.


                                              1. mayorovp
                                                20.09.2019 18:21

                                                Но это вы уже пошли копать в сторону Хаскель-специфичных фич.


                                                1. 0xd34df00d
                                                  20.09.2019 18:32

                                                  Ну я-то не знаю, что из этого в расте можно.


                                1. math_coder
                                  20.09.2019 18:08

                                  Понятно, что у кнопки свои поля есть. Не вижу как это мешает. https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=5c3b78efb712ccbfa84663ca4dc7c620


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


                                  1. mayorovp
                                    20.09.2019 18:23

                                    Ну так вот именно эту часть писать и не хочется:


                                    impl Widget for ButtonRuntime {
                                        fn set_visible(&mut self, v: bool) { self.widget.set_visible(v); }
                                        fn show(&mut self) { self.widget.show(); }
                                    }

                                    Собственно, это и есть тот бойлерплейт, которого неизбежно будет много.


                                    1. math_coder
                                      20.09.2019 18:25
                                      +1

                                      Да, это бойлерплейт. Он решается кострукцией а-ля impl Widget for ButtonRuntime through self.widget;. Я думаю есть все шансы, что она появится.


                                      1. mayorovp
                                        20.09.2019 18:27

                                        Я тоже так думаю, проблема в том что прямо сейчас её нет.


                                        1. math_coder
                                          20.09.2019 18:40

                                          Я как раз пишу сейчас нечто cхожее в этом плане с библиотекой виджетов и думаю попробовать соорудить что-то из макросов. Но это надо ещё думать как провернуть.


                                          1. Antervis
                                            20.09.2019 19:08
                                            +1

                                            можно попробовать определять методы в трейте IWidget через self.get_widget().foo(), а в «наследниках» переопределять уже только get_widget() -> &mut Widget. Так можно уменьшить число методов, которые придется переопределять «наследниках». Некоторое кол-во бойлерплейта всё равно будет, но оно уже будет линейно N классов, а не полиномиально от N классов и M их методов.


                                            1. math_coder
                                              20.09.2019 21:13

                                              Более того, это видимо и единственный вариант, так как наследования трейтов в расте тоже в существенной степени нет, это я стормозил.


                                              1. math_coder
                                                20.09.2019 22:01

                                                1. technic93
                                                  21.09.2019 00:15

                                                  Выглядит это ужасно.


                                                  1. math_coder
                                                    21.09.2019 00:25
                                                    +1

                                                    Это истинная личина ООП :-)


                                      1. lain8dono
                                        21.09.2019 05:04

                                        https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=b83454abccb677b49d32de627ca96403


                                        Если будете пихать это в свои макросы, то назовите его awesome_fortran. А вообще эту фигню можно запилить через процедурные макросы. Возможно это даже есть уже.


                                        1. technic93
                                          21.09.2019 12:12

                                          А с двумя типами получится через Deref, типа множественное наследование? Хотя это более редкий случай.


                                        1. math_coder
                                          21.09.2019 14:00

                                          Если будете пихать это в свои макросы

                                          Нет, это обсуждение подтолкнуло меня к следующим выводам.


                                          1. Такой подход к построению библиотеки виджетов и аналогичных систем требует всей мощи "классического" (как в C++/Java/C#) ООП.
                                          2. Если вам нужно ООП целиком, а не отдельные его паттерны, хорошо выразимые другими средствами (ФП, АП и т. д.), вы что-то делаете неправильно.
                                          3. Я делаю неправильно, надо искать другие подходы.

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


                                          1. lain8dono
                                            21.09.2019 18:08

                                            Я делаю неправильно, надо искать другие подходы.

                                            https://gitlab.redox-os.org/redox-os/orbtk например.


                                            1. technic93
                                              21.09.2019 18:49

                                              Там если глянуть на все эти impl Abc for Widget то будет примерно то что math_coder выше предлагал.


                                            1. math_coder
                                              21.09.2019 19:18

                                              Да, спасибо, на это стоит посмотреть.


                          1. Groramar
                            21.09.2019 00:27

                            (впрочем, с наследованием нормально умеет только C++, похоже).

                            Delphi отлично справляется. К слову, UB на нем вообще нет.


                        1. PsyHaSTe
                          21.09.2019 09:58

                          Ну, как и любая копипаста в расте эта решается макросами.


                          1. Antervis
                            22.09.2019 02:59

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


                            1. PsyHaSTe
                              22.09.2019 03:11

                              Ну да, но не выходит за границы юзабельности. Условно 20 лишних строчек чтобы сделать наследование 20 классов.


    1. PsyHaSTe
      19.09.2019 01:14
      +3

      Обычный код на С++ вытеснен Rust'ом не будет. А зачем?

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


      А вот новички так не умеют. Поэтому иметь язык, где новичок гарантированно не запишет вам мимо буфера и не разыменует нулл где не надо очень ценно. Не говоря о том, что компилятор очень хорошо объясняет, что не так => экономия времени сениора на вопрос "а почему я не могу тут в возвращаемой лямбде на локальную переменную сослаться".


      1. tangro
        19.09.2019 10:33

        Я имел в виду, что уже существующий прикладной код на С++ мало смысла переписывать на Rust (за исключением нескольких определённых областей, где никакого шанса улучшить надёжность нельзя упускать). А новые проекты или модули — да, весьма вероятно будут начинаться на Rust всё чаще.


        1. Revertis
          19.09.2019 12:34

          Firefox уже вовсю переписывается на Rust.
          Недавно пробовал его собирать, так там просто дофигища растового кода.


          1. tangro
            19.09.2019 12:56

            У Firefox свои причины, которые подойдут не всем:
            1. Они переписывают на Rust в основном только то, что и так собирались переписывать по каким-то причинам (переход на новую архитектуру, разбиение на модули, убирание lagacy)
            2. Они переписывают на Rust те компоненты, в которых у них была длинная история секьюрити-багов
            3. Это всё-таки opensource и люди могут писать на Rust, ну, потому что им прикольно писать на Rust.


            1. vvzvlad
              20.09.2019 12:45

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


    1. 0xd34df00d
      19.09.2019 02:57

      Раст всё-таки слабоват для таких вещей с точки зрения гарантий. Есть (и используются) более надёжные языки.


      1. tangro
        19.09.2019 10:31

        это какие?


        1. 0xd34df00d
          19.09.2019 16:36

          Coq, Agda, Lean какой-нибудь. Потенциально тот же Idris.


          1. mkpankov
            20.09.2019 09:34

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


            1. 0xd34df00d
              20.09.2019 16:27

              Суровая академия — это папиры с описанием того, почему на самом деле эти языки дают какие-то там гарантии.


      1. PsyHaSTe
        19.09.2019 11:38

        Если вам нужен язык с гарантиями и без латентности и низким профилем потребляемой памяти то лучше языка не найти :dunno:


        Когда тонна памяти и можно гхц/жвм поднять это другой вопрос.


        1. 0xd34df00d
          19.09.2019 16:38

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


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


          1. PsyHaSTe
            19.09.2019 17:05

            Я же говорю "если нужен". Если не нужен, то тут и вопроса нет.


      1. poslannikD
        19.09.2019 14:34

        Можно пример более надежных языков?


        1. 0xd34df00d
          19.09.2019 16:38

          Написал тут выше.


  1. Keynessian
    19.09.2019 09:48

    Есть ли какая-нибудь библиотека для Rust позволяющая работать с ООП?
    Rust — мне очень нравится, но очень смущает то как в нём обстоит с объектами.
    image


    1. skrimafonolog
      19.09.2019 09:55

      Просто не тяните целиком концепции из одного языка в другой — и все у вас будет хорошо.
      В Rust нет объектов, но есть вполне себе сходная по функционалу концепция широко известная по прочим языкам как «интерфейсы».


    1. vitvakatu
      19.09.2019 10:09

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


      1. Keynessian
        19.09.2019 15:22

        А как с поддержкой Vulkan API в Rust?
        image
        (или хотя бы с поддержкой триады OpenGL/OpenAL/OpenML, если с поддержкой Вулкана вдруг не очень)


        1. vitvakatu
          19.09.2019 15:37

          С этим очень круто, обратите внимание на проект gfx-rs: https://github.com/gfx-rs/gfx и на wgpu-rs: https://github.com/gfx-rs/wgpu


          Если коротко — поддерживаются по факту все современные и не очень GAPI, у некоторых — несколько реализаций (как в случае с Vulkan, есть gfx и есть vulkano). Раст также оказался на острие прогресса в современной Web-графике.


    1. PsyHaSTe
      19.09.2019 11:40

      Ну картину 1в1 можно перенести, все интерфейсы это трейты будут, а реализации — структуры.


  1. NikitOS9
    19.09.2019 12:04

    >4. Rust никогда не обгонит C/C++ по скорости
    по сути сравнивается c llvm (opt и llc) же.
    сейчас (может так и останется) rustc это фронт для llvm или это не так?


    1. Revertis
      19.09.2019 12:37

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


  1. eao197
    19.09.2019 12:53

    После прочтения остался в недоумении: стереотип "Rust — ещё один "убийца C/C++"" таки был развеян или, напротив, подтвержден? Не понятно, то ли Rust не убийца, то ли убийца, но не ещё один.


    1. Cerberuser
      19.09.2019 12:57

      Я так понял, что в стереотипе слова "убийца C/C++" понимаются в ироническом ключе — типа "замахнулся на святое, а сам-то...", и "развеивание" заключается в том, что замахнуться он как раз вполне себе вправе, пусть и не сейчас, а на перспективу.


      1. Gymmasssorla Автор
        19.09.2019 14:36

        Ключевое слово — "ещё один". В статье я показал, что Rust — не "ещё один", а действительно способен вытеснить C++ в некоторых сферах.


  1. embden
    19.09.2019 13:16

    А как там дела у раста с кроссплатформенным GUI? Чтобы Linux, Windows, Android?


    1. vitvakatu
      19.09.2019 13:50

      Плохо. Как дела с этим у всех остальных языков, кроме C++, Java, JavaScript и C#? И кстати почему забыли iOS и Mac?


      1. PsyHaSTe
        19.09.2019 13:57

        У сишарпа тоже все плохо, и у джавы емнип. Только электрон и мб Qt. Всё.


        1. KanuTaH
          19.09.2019 13:58

          А у сишарпа плохо? Я вот пользуюсь Keepass2Android, он вроде на Mono, нет?


          1. PsyHaSTe
            19.09.2019 14:04

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


          1. mayorovp
            19.09.2019 14:12

            Если он 2Android, то он на Xamarin (да, запускается оно под Mono, но библиотека не настолько кроссплатформенная).


            1. KanuTaH
              19.09.2019 14:14

              Ну да, я сейчас посмотрел — он на Xamarin.


        1. Groramar
          21.09.2019 00:33

          У сишарпа тоже все плохо, и у джавы емнип. Только электрон и мб Qt. Всё.
          Delphi/Lazarus еще.


          1. PsyHaSTe
            21.09.2019 10:00

            Не знаю как у вас, а в моем представлении дельфи давно мертв. Очень уж он неудобный в 2019-то году. Писал на нем весь универ 6 лет, но пора бы и честь знать.


            1. Groramar
              21.09.2019 11:20

              Нормально всё с Делфи и у нас и у вас. В чем неудобство то? Отлично работает. Под все платформы уже и давно.


              1. PsyHaSTe
                21.09.2019 12:32

                Ну давайте далеко за примером ходить не будем. Вот допустим у нас есть дерево ID комментариев и мы хотим из него получить дерево самих комментариев. То есть Tree<CommentID> -> Tree<Comment>. Функция загрузки комментария с сервера асинхронная, по одному айди один комментарий (нет, нельзя просто передать список ID чтобы сервер их все сразу вернул).


                Как это будет выглядеть на дельфи? На современных ЯП это 1-2 строчки с простым кодом.


                1. eao197
                  21.09.2019 13:34

                  На современных ЯП это 1-2 строчки с простым кодом.

                  Правда всего 2 строчки? Можно пример?


                  1. PsyHaSTe
                    21.09.2019 13:46

                    Ну примерно так


                    get_tree_comments_async :: Tree CommentId -> Async Tree Comment
                    get_tree_comments_async = traverse get_comment_by_id_async

                    или на скалке


                    def get_tree_comments_async(tree: Tree[CommentId])
                    : Async[Tree[Comment]] = 
                       tree.traverse(get_comment_by_id_async)

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


                    1. eao197
                      21.09.2019 14:06

                      Ух, отлегло. Я уж грешным делом подумал, что код get_comment_by_id_async будет занимать всего 2 строчки простого кода.


                      1. PsyHaSTe
                        21.09.2019 16:14

                        Ну там будет однострочник вызова сгенеренного сваггером клиента. Тоже не big deal.


                1. Groramar
                  21.09.2019 17:01

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


                  1. PsyHaSTe
                    21.09.2019 17:30

                    Ок. Если вдруг выложите — добавляйте коммент, посмотрим.


      1. embden
        19.09.2019 13:57

        Мне просто необходимо было написать приложение для этих трёх платформ. Выбор был между C++ с Qt и rust с чем-то не очень пригодным к использованию. Rust отложен в долгий ящик — нету у него пока возможностей тягаться с C/C++ в интересной мне области.


        1. vitvakatu
          19.09.2019 14:03

          Да, для GUI Раст сейчас не подходит, просто нет для этого инструментов.


        1. TargetSan
          19.09.2019 14:22

          Справедливости ради прикиньте, сколько человеколет и 20-футовых контейнеров денег вбухано в Qt — который начали разрабатывать в 91м, а первый релиз был в 95м. А для раста GUI сейчас пилится в основном энтузиастами по 1-2 человека.


    1. lain8dono
      19.09.2019 15:15

      Да с любым GUI у нас всё очень плохо. Не только с кроссплатформенным. Можете глянуть https://areweguiyet.com/. Чего не хватает для хорошего полноценного GUI? Нормальной обработки текста, вот чего. Н???е???л???????ь?з??я??????? ????п????????ро?с????т?????о???? ????????вз????я???????т??ь???? ??????и?? ???н???ари?с?????о????????в???а??ть????? ?????п?р??о??????и?зв????????ол???????ь??н????у????????ю?????? ???????п??о????????с??????л??е????????д????????о??????в??????ат???????е????л?????ь????н??о???с????т???????ь????? ???г??????л???и??ф??????о???????в??.???
      Такие дела. Ждём новостей от https://github.com/linebender/skribo.


  1. Sodah
    19.09.2019 14:31

    А кто-нибудь может развенчать стереотипы по поводу Dart?


  1. rwscar
    19.09.2019 14:31

    Я наверное ещё недостаточно хорошо понимаю в программировании, но всё же.
    Может ли кто-нибудь, пожалуйста, пояснить, в чём профит использования минималистичных ключевых слов и прочих языковых элементов в дизайне ЯП?
    Как по мне, так все эти fn, struct, mut, скобочки всех возможных видов и знаки пунктуации в огромных количествах делают язык очень похожим на Brainfuck и визуально сложным.


    1. Gymmasssorla Автор
      19.09.2019 14:32
      +1

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


    1. TargetSan
      19.09.2019 15:56
      +1

      Моё личное мнение как человека, пишущего в основном на С++.


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


      Знаков пунктуации в реальном коде получается ставнительно немного. В том же Go мне например хотелось бы их чуть побольше. Ну а с С++ вообще не сравнить.


      1. Siemargl
        19.09.2019 18:43
        -2

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

        Например

        macro_rules! with_handles {
            ([($handle_name: ident: $unhandle_name: block)] => $body: block) => {
                $unhandle_name.run(|$handle_name| {
                    $body
                })
            };
            ([($handle_name1: ident: $unhandle_name1: block),
              ($handle_name2: ident: $unhandle_name2: block),
              $($rest: tt)*] => $body: block) => {
                $unhandle_name1.run(|$handle_name1| {
                    with_handles!([($handle_name2: $unhandle_name2), $($rest)*] => $body)
                }).and_then(|n: $crate::utils::HandleResult<_>| n)
            };
            ([($handle_name: ident: $unhandle_name: block), $($rest: tt)*] => $body: block) => {
                $unhandle_name.run(|$handle_name| {
                    with_handles!([$($rest)*] => $body)
                }).and_then(|n: $crate::utils::HandleResult<_>| n)
            };
        }

        Или примеры попроще прямо из учебника
        // парсинг массива строк
        {
            let strings = vec!["tofu", "93", "18"];
            let (numbers, errors): (Vec<_>, Vec<_>) = strings
                .into_iter()
                .map(|s| s.parse::<i32>())
                .partition(Result::is_ok);
            let numbers: Vec<_> = numbers.into_iter().map(Result::unwrap).collect();
        }
        // безопасная с обработкой ошибок ф-ция sqrt(ln(x / y))
        fn op(x: f64, y: f64) -> f64 {
            match checked::div(x, y) {
                Err(why) => panic!("{:?}", why),
                Ok(ratio) => match checked::ln(ratio) {
                    Err(why) => panic!("{:?}", why),
                    Ok(ln) => match checked::sqrt(ln) {
                        Err(why) => panic!("{:?}", why),
                        Ok(sqrt) => sqrt,
                    },
                },
            }
        }


        1. mayorovp
          19.09.2019 19:43

          Последний пример полностью бессмысленен.



        1. PsyHaSTe
          19.09.2019 20:24

          Ну с такими примерами конечно раст страшный.


          А ведь если переписать нормально, то будет выглядеть так:


          fn op(x: f64, y: f64) -> f64 {
              checked_div(x, y)
                  .and_then(checked_ln)
                  .and_then(checked_sqrt)
                  .unwrap()
          }

          playground


          1. Siemargl
            19.09.2019 22:15
            -1

            Вы Меня попрекаете примерами из офсайтовского учебника с rust-lang ???

            Вы в своем уме? Хотя вопрос риторический.


            1. red75prim
              19.09.2019 22:40
              +1

              Там же на следующей странице приводится пример, как избавиться от этой пирамиды match'ей.


          1. lain8dono
            21.09.2019 05:22

            А ещё лучше вот так:


            fn op(x: f64, y: f64) -> Result<f64, String> {
                use checked::{ln, div, sqrt};
                sqrt(ln(div(x, y)?)?)
            }

            БЕЗ ПАНИКИ! Теперь откройте следующую страницу путеводителя по галактике.


            1. PsyHaSTe
              21.09.2019 10:01

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


    1. math_coder
      19.09.2019 17:18
      +3

      let mutable my_variable: Option<&mutable dynamic MyVerySpecificTrait> = &mutable some_variable.as_reference().lock().unwrap().as_reference();
      и
      let mut my_var: Option<&mut dyn MyTrait> = &mut some_var.as_ref().lock().unwrap().as_ref();


      Или


      function some_function(input: implements Function(implements IntoString) -> Box<dynamic SomeTrait>) -> implements Iter<Item=ReferenceCell<integer_16bit>>
      и
      fn some_fn(input: impl Function(impl IntoString) -> Box<dyn SomeTrait>) -> impl Iter<Item=RefCell<int16>>


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


    1. DreamingKitten
      19.09.2019 18:59

      Это, к.м.к. некий разумный компромисс между слишком уж словоохотливыми языками типа Паскаля, со всеми этими begin-end, repeat-until, unit-interface-implementation-initialization-end и тому подобного. Даже с автокомплитом и копипастами набиваешь эти многотомники и чувствуешь как на каждой строчке у тебя крадётся по полсекунды времени; и языками, по которым проводится ежегодный International Obfuscated Code Contest.


      1. Groramar
        21.09.2019 00:40

        Паскаль сейчас в своих реализациях очень хорошо оптимизирован, что бы bloated code вообще не замечать при наборе. Например begin/end ставится просто хоткеем, удобно, всегда так и делаю. Читать конечно это всё сильно удобнее, чем плюсы и остальные птичьи языки.


        1. PsyHaSTe
          21.09.2019 10:02
          -1

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


          1. Groramar
            21.09.2019 10:32

            Читаемость Паскаля — один из краеугольных камней языка же :) О чем вы. Читаемость на уровне почти обычных предложений английского языка. И, на минуточку, после 7-ки 20 лет уже прошло :)


            1. PsyHaSTe
              21.09.2019 11:07

              Читаемость Паскаля — один из краеугольных камней языка же

              Ну вот я её не ощущаю. После того как перешел на шарпы стало намного проще и читаемее. Особенно объявление пропертейв паскале было стремным. Хелсберг одобряе.


              1. Groramar
                21.09.2019 11:25

                А что не так с пропертями? Вроде прозрачно всё. Да и как-то я больше по коду — проперти записал и забыл. Я в них хорошо если 0.1% времени смотрю. Скорее меньше.
                К слову — существую редакторы свойств, кому лень руками набирать. Свойства можно добавляють в один хоткей чаще всего (2 нажатия — хоткей + Enter).
                Повторюсь: bloated сейчас максимально автоматизирован.


  1. Alesh
    19.09.2019 15:17

    Ага именно «элегантная абстракция» стало решающим в прекращении изучения Rust. На уровне хелоуворда вопросов нет, все красиво и логично. Но более сложные конструкции вызывают взрыв мозга. Темплейты С++ ничем не лучше конечно, но что бы «ощутить их мощь», надо серьезно заморочиться с кодом. Многие пишущие на Плюсах знают о темплейтах только верхушки и прекрасно пишут. А вот с Растом, шаг влево, шаг вправо от хелоуворда, и здравствуйте я ваша тетя :) Может конечно что изменилось за три года, но вроде как нет, только усугубилось.


    1. vitvakatu
      19.09.2019 15:47
      +1

      Чисто ради моего интереса — что конкретно имеете в виду? Насколько я понимаю, вы про лайфтаймы? Мне правда интересно увидеть примеры кода, который технически оправдан и при этом вызывает взрыв мозга.


      Для читателей комментов: у меня вот тут сейчас есть проект, в котором более 100 тыс. строк кода исключительно на расте написаны. Точное количество лайфтаймов в этой кодовой базе — 152, за исключением &'static. Если вместе с ним — 191. И почти во всех этих случаях использование абсолютно тривиально, я даже думаю от многих использований можно избавится в современных версиях компилятора.


  1. rboots
    19.09.2019 16:59
    +1

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


  1. Antervis
    19.09.2019 19:31
    -2

    Функция safe_display() полностью безопасна, т.к. правильность потенциально небезопасного блока формально доказуема

    а если не доказуема?

    В некоторых синтетических тестах производительности Rust даже обгоняет GCC C:

    Когда кто-то сравнивает языки программирования замеряя перф разных библиотек:
    image
    А если взять все 100500 библиотек плюсов/си, сопоставить их с аналогами на расте и замерить, в скольки процентах случаев выиграет раст?

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


    1. Gymmasssorla Автор
      19.09.2019 19:58
      +1

      а если не доказуема?

      Значит программист неправильно написал Unsafe блок, который впоследствии породит UB.


      А если взять все 100500 библиотек плюсов/си, сопоставить их с аналогами на расте и замерить, в скольки процентах случаев выиграет раст?

      Я и не утверждал, что Rust всегда быстрее C/C++.


      1. Antervis
        20.09.2019 11:46

        Значит программист неправильно написал Unsafe блок, который впоследствии породит UB.

        Как формально доказать, что использование функции из 3rdparty библиотеки всегда корректно? Особенно когда это проприетарная библиотека? Всё-таки «ошибка не в моём коде» != «моя программа работает корректно»

        Я и не утверждал, что Rust всегда быстрее C/C++.

        а смысл выбирать из всего множества только те пару библиотек где раст «даже обгоняет плюсы»?


        1. mkpankov
          20.09.2019 12:29

          Проприетарные библиотеки в смысле "нам кто-то дал бинари, но не исходники" в экосистеме Rust пока не использует никто. Там и с распространением будут проблемы. С другой стороны, а чем это хуже проприетарной библиотеки на C или C++?
          А на тему аудита экосистемы есть интересные инициативы: 1 2.


          1. Antervis
            20.09.2019 13:48

            Проприетарные библиотеки в смысле «нам кто-то дал бинари, но не исходники» в экосистеме Rust пока не использует никто

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

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


            1. mkpankov
              20.09.2019 14:12

              К winapi естественно используются биндинги, так что всё интересное там происходит на стороне C++-тулчейна, а не раста.


        1. Gymmasssorla Автор
          20.09.2019 14:57

          Как формально доказать, что использование функции из 3rdparty библиотеки всегда корректно? Особенно когда это проприетарная библиотека? Всё-таки «ошибка не в моём коде» != «моя программа работает корректно»

          А никак, это делать должен создатель 3-rd party программного обеспечения, как и во всех других языках. Не понял что Вы хотите услышать. Unsafe блок либо может породить UB (если его с ошибкой написали), либо нет (если его написали правильно).


          а смысл выбирать из всего множества только те пару библиотек где раст «даже обгоняет плюсы»?

          Смысл в опровержении "Аналогичный код на Rust не может быть быстрее C/C++". Тут уже входят в дело бесплатные оптимизации о компилятора и zero-cost абстракции (что в синтетических тестах показано). Библиотеки были включены для более полного понимания картины.


          1. Antervis
            20.09.2019 15:41

            А никак, это делать должен создатель 3-rd party программного обеспечения, как и во всех других языках

            Всё-таки «ошибка не в моём коде» != «моя программа работает корректно»


            Смысл в опровержении «Аналогичный код на Rust не может быть быстрее C/C++»

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


            1. Gymmasssorla Автор
              20.09.2019 16:47

              Всё-таки «ошибка не в моём коде» != «моя программа работает корректно»

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


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

              Это не ограничение Rust, а скорее недоработка компилятора rustc, т.к. такой же код на Rust можно скомпилировать в меньший набор инструкций, ничего не мешает это сделать.


            1. PsyHaSTe
              21.09.2019 10:07

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

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


              А про принципиальную производительность например можно взять то что все ссылки noalias, поэтому возможны оптимизации "на местах" связанные с этим, включая приснопамятную копию строчки (s++ = x++). Программу на С где каждый указатель помечен через restrict я не встречал.


            1. PsyHaSTe
              21.09.2019 13:36

              Посмотрел доклад, там много к чему можно привязаться, но отмечу одну вещь:


              image


              Это прямая ложь. Unsafe "не выключает борровчекер", конкретно про это есть целая статья у клабника.


              Во-вторых конечно же на ансейф абстракциях можно строить сейф язык. Точно так же вы можете пользоваться "интами" и "флоатами" в С++, а на нижнем уровне у вас только адреса в памяти, и можно случайно прочитать инт как флоат и наоборот. Все программирование про то, как инкапсулировать сложность, и выставить набор безопасных АПИшек над потенциально опасным ресурсом (который при неправильном использовании может взорваться).


              Если человек говорит "написали ансейф — потеряли гарантии", то он просто не понимает о чем разоваривает.


              1. KanuTaH
                21.09.2019 14:51

                Ну так borrowchecker не распространяется на raw pointers, поэтому на слайде и написано «нет безопасности». А так-то да, абстракции стараются делать везде, но их ненулевая стоимость и протекаемость — это факт. Вот вы давеча писали про «бесплатный сыр» в виде тотального restrict, так ведь он не бесплатный, за него приходится платить использованием лишних абстракций типа того же RefCell, который не бесплатный. И что больше повлияет на эффективность конкретного кода — плюсы от тотального restrict или минусы от RefCell, который там в рантайме счётчики ссылок тусует — это большой вопрос, и зависит от специфики этого кода. Если у нас идёт интенсивная работа с большими мультисвязными структурами в куче, то, сдаётся мне, выигрыш от авторестрикта не покроет проигрыш от RefCell. Ну и, как я раньше уже говорил, сам по себе факт появления Cell означает, что основная абстракция borrowchecker'а «одной мутабельной ссылки в один момент времени всегда достаточно» УЖЕ протекла.


                1. PsyHaSTe
                  21.09.2019 16:17

                  Ну так borrowchecker не распространяется на raw pointers, поэтому на слайде и написано «нет безопасности».

                  99% кода это не raw pointers. В очередной раз в этом убедился, когда читал про разработку Midori. Люди писали на диалекте шарпов но пришли к тем же выводам, что и команда раста.


                  Вот вы давеча писали про «бесплатный сыр» в виде тотального restrict, так ведь он не бесплатный, за него приходится платить использованием лишних абстракций типа того же RefCell, который не бесплатный.

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


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

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


                  1. KanuTaH
                    21.09.2019 16:21

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

                    Ну, например, практически любой GUI — это «мутабельный стейт на ссылках» с многосвязными отношениями child/parent/siblings. Вы тоже скажете, что это «из плохого дизайна»? Так ведь другого нет-с.


                    1. PsyHaSTe
                      21.09.2019 16:21

                      Посмотрите на реакт. Там нет мутабельного стейта на ссылках.


                      1. KanuTaH
                        21.09.2019 16:25

                        Сам по себе DOM весь построен на ссылках. А то, что некоторые пытаются заметать этот факт под ковер и перегенерировать это ссылочное дерево каждый раз — ну, это такое, очень «быстрое» решение, ага.


                        1. PsyHaSTe
                          21.09.2019 17:32

                          Сам по себе DOM весь построен на ссылках.

                          А в транзисторах нет никаких интов и флоатов, но мы с ними как-то работаем.


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

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


                          Джава до 10 раз медленне плюсов, но тем не менее её используют. ЖС медленнее джавы на те же порядки, и его тоже используют.


                          1. KanuTaH
                            21.09.2019 17:52

                            Вы бенчмаркали? Емнип как раз с виртуальным деревом решение быстрее ручной перерисовки.

                            Смотря что вы подразумеваете под «ручной перерисовкой». Просто изменить напрямую в DOM пару значений НАМНОГО производительнее, чем сначала изменить их в VDOM, потом машинерия реакта там начнет шевелиться, вычислять diff'ы, вызывать там всякие render() и так далее. Вот кто-то бенчмаркал на мобилках:

                            image
                            image

                            Когда-то давно авторы react утверждали, что VDOM быстрее «реального» DOM:

                            image

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

                            Что касается «все используют» — ну да, используют. Но к месту жеж! Нельзя просто так говорить, что «мутабельный стейт на ссылках — ошибка дизайна». С точки зрения дремучего чистого FP это, может, и так, но есть же еще и реальный мир.


                            1. PsyHaSTe
                              21.09.2019 17:58

                              Судя по графикам реальная разница отрисовки возникает через после количества элементов на котором время отрисовки превышает 20 секунд. Могу согласиться по формальным признакам, но чисто практически что 30 что 45 секунд это неюзабельно.


                              А на количестве элементов меньше 1000 (так что время отрисовки меньше 20 секунд) разницы не наблюдается.




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


                              1. KanuTaH
                                21.09.2019 18:12

                                Судя по графикам реальная разница отрисовки возникает через после количества элементов на котором время отрисовки превышает 20 секунд.

                                Я в свое время мерял, но немного другое — меня интересовала RAM, так вот в Chrome на десктопе разница начинается где-то в полтора раза сразу (не в пользу реакта, само собой) там уже на сотне элементов, а дальше разница росла еще больше, могла и до трех раз доходить, когда счет элементов шел на тысячи. Короче, очередная дырявая абстракция, может, и без «мутабельного стейта на ссылках», но, как обычно, за определенную (и недешевую) цену.


              1. Antervis
                22.09.2019 02:46

                Если человек говорит «написали ансейф — потеряли гарантии», то он просто не понимает о чем разоваривает.

                unsafe перекладывает ответственность за корректность вложенного кода с компилятора на программиста. Если программист тоже не в состоянии гарантировать корректность этого блока кода (как например в случае вызыва unsafe функции из 3rdparty библиотеки), значит, блок потенциально скомпрометирован, как и весь код, от него зависящий (рекурсивно).


                1. PsyHaSTe
                  22.09.2019 03:11

                  Да нет, если функция не может проверить локально инварианты, то она сама unsafe. так и живем. Если вы сделали функцию которую вызывая из сейф раста можно поломать то это уб.


                  1. Antervis
                    22.09.2019 16:25

                    то есть, формально, весь стек вызовов, содержащий обращение к 3rd party библиотеке, должен быть размечен как unsafe? И что, помечают?


    1. An_Owl_or_not
      20.09.2019 02:30

      В расте надо выбирать написать код без unsafe или чуть более оптимальный внутри unsafe.

      Не раз слышал что unsafe-код заменяли на safe с выигрышем по производительности, так что не думаю что тут есть выбор между "написать код без unsafe или чуть более оптимальный внутри unsafe". Скорее есть возможность safe-код, скорость которого не устраивает, попытаться оптимизировать с помощью unsafe.


    1. lain8dono
      21.09.2019 06:42

      В расте надо выбирать написать код без unsafe или чуть более оптимальный внутри unsafe.

      unsafe не для оптимизаций. Он для ручного доказательства корректности. А ещё для того, чтоб можно было сделать grep -rn unsafe вместо запуска отладчика.


      А если взять все 100500 библиотек плюсов/си, сопоставить их с аналогами на расте и замерить, в скольки процентах случаев выиграет раст?

      Давайте, мне тоже интересно. Топ можете взять отсюда https://crates.io/crates?sort=downloads


      1. Gymmasssorla Автор
        21.09.2019 17:06

        Одно из применений Unsafe — как-раз таки оптимизация.


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


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


        1. lain8dono
          21.09.2019 19:27
          +1

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


          1. Antervis
            22.09.2019 00:58

            Само по себе использование unsafe ничего не говорит об оптимизациях

            тем не менее, некоторые оптимизации невозможны без unsafe


        1. humbug
          21.09.2019 20:23

          SIMD unsafe не из-за оптимизации, а из-за того, что компилятор не может гарантировать корректность доступа к данным (выравнивание и проч).


  1. amarao
    20.09.2019 14:14

    Кстати, сложность Rust можно понять, попытавшись реализовать свой тип данных со своим IntoIterator без использования итераторов других типов, только на замыканиях. Добавить lifetimes по вкусу.


  1. bubukerrr
    20.09.2019 19:42

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


    1. PsyHaSTe
      21.09.2019 10:09

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


      1. technic93
        21.09.2019 12:20

        Оффтоп

        Всегда было интересно сколько щупалец у существ которым "удобно на высокоуровневом ФП"


        1. PsyHaSTe
          21.09.2019 12:35

          Так ФП оно про то, как писать меньше, а получать больше, а не про башню из слоновой кости и "смотри как я могу физзбаззфпэдишн захреначить". За примерами далеко ходить не надо: генерики вместо темплейтов, паттерн матчинг вместо свича и ифчиков, АДТ вместо визиторов, лямбды вместо паттернов на каждый чих, итераторы с комбинаторами вместо ручных циклов… Это всё в мейнстрим из ФП пришло. Неужели это все иноплатеная хрень, и в циклах визиторы на ифчиках устраивать удобнее?


          "Не ну это все удобные вещи, которые мейнстрим взял, а остальное в ФП какая-то хрень" — да нет, просто это парадок блаба. Штуки к которым привыкли начинают считаться удобными и полезными, а остальные воспринимаются в штыки. Вон, постепенно мейнстрим доползает до Option/Either монад (в расте вся стд на них построена), АДТ и тайпклассов (пропозалы в ближайшие версии всех популярных языков уже есть), а там глядишь и HKT подтянется. Просто ФП раньше других получает штуки, которые получают распространение лет через 10. Но ничего не мешает быть эффективным уже сегодня, а не ждать когда в любимые плюсы/шарпики/тайпскрипт завезут желаемое.


          1. KanuTaH
            21.09.2019 15:23

            Если бы ФП было таким идеальным, то на хаскеле писали бы абсолютно все, включая графические движки, прошивки для микроконтроллеров или операционные системы. Но вот что-то не пишут (нежизнеспособные эксперименты типа HOUSE в расчет не берем, я о мейнстриме). И ведь даже нельзя сказать «это потому, что он черный новый» — ведь он появился еще в начале 90-х, а сейчас доля на гитхабе у него где-то 0.2%, и падает. Как думаете, почему?


            1. PsyHaSTe
              21.09.2019 16:21

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


              И ведь даже нельзя сказать «это потому, что он черный новый» — ведь он появился еще в начале 90-х, а сейчас доля на гитхабе у него где-то 0.2%, и падает.

              Ну ведь миллионы не могут ошибаться, не так ли?)


              1. KanuTaH
                21.09.2019 16:29

                Ну во-первых потому что хачкель не так хорош в условиях ограниченных ресурсов

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

                Ну ведь миллионы не могут ошибаться, не так ли?)

                Ну, бывает и обратная ситуация — "я Д'Артаньян, я эффективен уже сегодня, а вы все..." :) Ну это я так, шучу, конечно.


                1. PsyHaSTe
                  21.09.2019 17:36
                  +1

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

                  Да, но наконец софт начал использовать больше 1 ядра не только в архиваторах.


                  Ну, бывает и обратная ситуация — "я Д'Артаньян, я эффективен уже сегодня, а вы все..." :) Ну это я так, шучу, конечно.

                  Это просто мой опыт. Я наблюдаю как развиваются крупные фреймворки в ООП языках и они следуют тем же путем, который пионерят ФП языки. Я по сути делюсь информацией, чтобы люди могли где-то более правильное решение найти. А то вот в расточате рассказал про code first и EF миграции, а оказывается что люди-то и не слышали, всё ручками скрипты пишут… Технология есть, многим бы пригодилась, но люди не в курсе, что их боль вполне себе необязательная.


                  Как-нибудь соберусь с силами написать про фп статейку. А то столько мифов про "математику", а по сути все тривиально про то, как сделать композабельными сложные системы. Впрочем, об этом в другой раз.


                  1. Gymmasssorla Автор
                    21.09.2019 17:44

                    Напишите, с удовольствем почитаю. Сам начинаю потихоньку вкатываться в мир ФП.


                    1. PsyHaSTe
                      21.09.2019 22:29

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


                  1. KanuTaH
                    21.09.2019 17:56

                    Я наблюдаю как развиваются крупные фреймворки в ООП языках и они следуют тем же путем, который пионерят ФП языки.

                    Да, они берут из ФП кое-что. Но и о корнях не забывают, «чистый» ФП там сам по себе тоже не нужен.


                    1. PsyHaSTe
                      21.09.2019 17:59

                      В том числе и для вас напишу. Расскажу про мутабельный стейт в чистом ФП, про сайд эффекты которые не эффекты. Вот это все.


                      1. KanuTaH
                        21.09.2019 18:04

                        Хорошо, хорошо :)


              1. DreamingKitten
                21.09.2019 16:34

                А во-вторых потому что гугл проплатил го, а не хачкель, который делают 1.5 энтузиаста.
                Ээээ, но ведь го это не ФП, как и зачем вы их сравниваете?
                А так да, хачкель на обе лопатки уделывает го во всех сферах, в которых его рекламируют,
                В микросервисах, например, да? Представил себе gRPC на хаскеле, брррррр…


                1. KanuTaH
                  21.09.2019 16:44

                  Ээээ, но ведь го это не ФП, как и зачем вы их сравниваете?

                  Ну так видимо поинт в том, что вот мол фп-язык как-то где-то что-то выиграл у не-фп языка, поэтому фп-язык перенимает звание сильвер буллета у не-фп языка, а не-фп язык занимает почетное второе место с титулом «хреновая фигня».


                1. PsyHaSTe
                  21.09.2019 17:38
                  +2

                  Ээээ, но ведь го это не ФП, как и зачем вы их сравниваете?

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


                  В микросервисах, например, да? Представил себе gRPC на хаскеле, брррррр…

                  А что не так-то? У нас есть такой сервис, на скале правда, но на хачкеле прототип показал себя в 10 раз перфоманснее и в 10 раз меньше памяти, при лучшей читаемости. Так на него бы и перешли, если б раст не показал себя еще лучше на той же нагрузки.


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




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