Опровергаем четыре стереотипа о языке программирования Rust


    Язык программирования 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, в действительности, — это подмножество языка Rust, разрешающее совершать лишь четыре операции, запрещённые в "безопасном" Rust. Borrow-checker не перестаёт работать, по-прежнему нельзя двум переменным обладать тем же значением и так далее. Запрещены лишь:


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

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


    // Эта функция не породит UB никогда:
    fn safe_display() {
        unsafe {
            let x = 385;
            let x_ref: *const i32 = &x;
            println!("{}", *x_ref);
        }
    }



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




    И ещё одна трактовка: "Unsafe не работает, потому что любая программа на Rust использует небезопасные библиотеки на Си". Это заблуждение аналогично следующему: "Безопасность Java не работает, потому что любая прикладная программа исполняется на ОС, которая может содержать UB".


    Дело в том, что если библиотека на Си не содержит UB, то она ни в коем случае не породит UB в использующим её коде на Rust. Существуют некоторые "оговорки", например, "UB может возникнуть как следствие неправильного использования безопасной библиотеки на Си", но если обобщить, то библиотека не является безопасной. Смотреть на определение UB из Википедии:


    Другими словами, спецификация не определяет поведение языка (библиотеки, микросхемы) в любых возможных ситуациях, а говорит: «при условии А результат операции Б не определён».

    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 являются restrict по-умолчанию, что открывает простор для оптимизаций компилятором. Добиться такого же эффекта в C/C++ можно, но только если пометить все иммутабельные ссылки/указатели как restrict, что в реальном коде катастрофически снижает читабельность.


    Заключение


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


    Поддержать автора
    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 613

      +14

      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

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

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

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

            +2

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


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

              +5

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


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

                +1

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

                  +2
                  Вобщето нет. Факт чтения не обезателен для получения UB.
                  Например
                  let x = unsafe{  std::mem::uninitialized::<bool>() };
                  

                  Этот код UB
                    –1

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

                      +4

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


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


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

                        0

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

                          +1

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

                            0

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

                                –2

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


                                Если в ссылке оказался 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.

                                  0

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

                                    0

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

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

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

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

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

                                        0

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

                                          –1

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

                                            0

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

                                              0

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

                                                0

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

                                                  +1

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

                                                    0

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

                                                      0

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

                                                        0

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

                                        +2

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

                                          +2

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

                                    +1

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

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

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

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

                                Вообще это интересно, что вы коснулись 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.

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

                                    Нет, в Расте внутреннее представление 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.
                                      0
                                      а если скастовать 2 в bool, что будет?
                                        0

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

                                        0

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

                                          0

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

                                          +1

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

                                            +1

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

                                          0

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


                                          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).
                                          0

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


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


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

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

                                            0

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

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

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

                                    +4

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


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

                                      –3

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

                                        0

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

                                          0

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

                                        +4

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


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

                                          –8

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

                                            +6

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

                                              +9
                                              Любое IO — undefined behavior

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

                                          0

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

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

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

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

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

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

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

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

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

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


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


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

                                          +7

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

                                            0

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

                                              0

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

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

                                                Это связано не только с языком, а еще и с тем где ты его используешь, где работаешь.
                                                  –3
                                                  А что странного. Грубо говоря, раст особенно нравится тем, кто не смог в C++. Профессиональному плюсовику раст создает больше неудобств, чем помогает.
                                                  Ну и не надо рассматривать вместе C и современный C++. В качестве замены C раст возможно вполне себе.
                                                    +2

                                                    Я писал на С++ пять лет. Смог. До сих пор удивляюсь, насколько стройным и удачным получился Rust. Зачем вы меня оскорбляете?

                                                      +4

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


                                                      1. Имеющаяся кодовая база на плюсах.
                                                      2. Незнание раста окружающими людьми.
                                                      3. Писать действительно высокопроизводительный код нужно не так часто, а там, где жёстких требований к этому нет, можно использовать более мощные, чем раст или плюсы, языки.
                                                        +1
                                                        Для меня неудобством оказалась необходимость постоянно объяснять компилятору borrowing, с которым у меня и на плюсах нет проблем. Код на расте выглядит замусоренным дополнительными бесполезными деталями, которые при чтении надо отфильтровывать. Ну и объем выполняемой в голове работы в процессе написания кода больше.
                                                        Да, мой опыт с растом ограничился неделей между сменами работы примерно 2.5 года назад. Я сейчас его благополучно полностью забыл, объясню по памяти на пальцах почему решил не продолжать.
                                                        Было примерно следующее:
                                                        Если на С++ мы имеем foo(Object const &obj), то для foo не имеет значения, откуда пришел obj: со стека, статический, из кучи. Насколько помню, на расте, когда я захотел поменять стековую переменную на динамически аллоцируемую, мне пришлось менять описание параметра foo.
                                                        То есть: элементарный рефакторинг потребовал неоправданных усилий.
                                                        Я что-то делал не так?
                                                          +3
                                                          Я что-то делал не так?

                                                          Да. Ссылки от переменных на стеке и от переменных в куче не отличаются.


                                                          Ну то есть у вас могло быть


                                                          #[derive(Debug)]
                                                          struct Foo {}
                                                          fn foo(f: &Foo) {
                                                              dbg!(f);
                                                          }
                                                          fn main() {
                                                              let f = Foo{};
                                                              foo(&f);
                                                          }

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


                                                          И


                                                          #[derive(Debug)]
                                                          struct Foo {}
                                                          fn foo(f: &Foo) {
                                                              dbg!(f);
                                                          }
                                                          fn main() {
                                                              let f = Box::new(Foo{});
                                                              foo(&f);
                                                          }

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


                                                          Как видите, разница только в создании переменной на стеке или в куче.

                                                            +3
                                                            Для меня неудобством оказалась необходимость постоянно объяснять компилятору borrowing

                                                            В современном расте много где вообще забываешь про то, что оно есть.


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

                                                            И что вам пришлось поменять в этом коде?


                                                            // некая функция
                                                            fn foo(obj: &Object) { ... }
                                                            
                                                            // что-то на стеке
                                                            let obj = Object { ... };
                                                            foo(&obj);
                                                            
                                                            // нечто статическое
                                                            static OBJ: Object = Object { ... };
                                                            foo(&OBJ);
                                                            
                                                            // что-то из кучи
                                                            let obj = Box::new(Object { ... });
                                                            foo(&obj);

                                                            Я что-то делал не так?

                                                            Не отличили передачу по ссылке и передачу по значению? Варианта там ровно два.

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

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


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

                                                              0
                                                              Как раз с возможностями у него ИМХО не очень — особенно если смотреть в разрезе возможностей использования уже готового стороннего кода, количество которого, мягко говоря, несравнимо у C/C++ и у раста. Да, в теории можно напедалить биндингов и оберток к интересующим библиотекам самостоятельно, но… положа руку на сердце — стоит ли оно того? Каждый отвечает на этот вопрос сам, но лично мне этим заниматься просто не интересно, не очевидна выгода в плане конечного выхлопа.
                                                                +4

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


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


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

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

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


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

                                                                      –1

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

                                                                        0

                                                                        Вопрос про качество либ. Например я лично оборачивал opencv (cv-rs), и никаких проблем у меня не возникло. Хотя там и коллбечные апи есть, и эксепшны, и вот это все.

                                                                          –1

                                                                          "Вполне себе рабочая библиотека" != "библиотека, которую можно использовать, не трахая себе мозг тысячами страниц требований". Rust просто трахает мозг программисту на этапе компиляции, вот и всё.

                                                                            –1
                                                                            Ну вот он и затрахал бессмысленными (в данной ситуации) хотелками до такой степени, что его выкинули на помойку. Причем автор — не новичок в расте, и конкретно этот свой Way Cooler он пилил на расте годами — судя по блогу, первая альфа вышла еще в 2016. По факту — работал на помойку.
                                                                        +3
                                                                        в которой ты тратишь основное время не на написание продуктивного работающего кода, а на бесконечные попытки угодить компилятору.

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


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

                                                                    –1
                                                                    Ну раст и правда помогает «неосиляторам», которые не умеют держать 1000 страниц спеки в голове каждый раз, когда печатают на клавиатуре. Другой вопрос, что я сильно совмневаюсь в подобных возможностях людей, которые заявляют подобное.

                                                                    1000 страниц спеки держать в голове, когда пишешь код, не надо. Современное прикладное программирование на С++ — это процентов на 80% последний Мейерс.
                                                                    Должна быть база в виде С (т.е. можешь в принципе переписать на нем любой свой C++ код?).
                                                                    Да, и знание алгоритмов — в объеме понимания того, что под капотом у std контейнеров — для осознанного их применения.
                                                                    Этого достаточно, чтобы с помощью современных IDE писать надежные С++ программы, не держа в голове 1000 страниц спеков.
                                                                      +2

                                                                      Я видел экпертов по плюсам с 20-летним опытом, которые кэшировали итератор из которого удаляли элементы. От ошибок никто не застрахован.


                                                                      Как говорится новички совершают простые ошибки, а эксперты не простые.

                                                                        +2

                                                                        Нет, этого не достаточно.


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


                                                                        Я темплейты, конечно, люблю, но за то время, что я их освоил, можно было бы и 1000-2000 страниц спеки выучить.

                                                                    0
                                                                    А что странного. Грубо говоря, раст особенно нравится тем, кто не смог в C++. Профессиональному плюсовику раст создает больше неудобств, чем помогает.

                                                                    не совсем. Раст разрабатывался на замену с++03 и решил многие его проблемы by design. Фишка в том, что к моменту релиза раста (2015-й год) большинство проблем c++03 были решены в с++11/14. Не окончательно, но следуя несложным гайдлайнам (примерно тем же принципам, что форсит компилятор раста) число болезненных ошибок плюсов скатывается в ноль.

                                                                    Мне любопытен раст, но я (вспоминая все свои UB за последние пару лет) не нахожу переход на него столь целесообразным. И мне кажется, что наиболее ярые поклонники раста — те, кто попросту не успел пописать на современных плюсах.
                                                                      +1

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

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

                                                                        Особенно вот этот несложный гайдлайн мне очень нравится.


                                                                        — Пожалуйста, избегайте гонок данных.
                                                                        — Да, мой капитан!


                                                                        И мне кажется, что наиболее ярые поклонники раста — те, кто попросту не успел пописать на современных плюсах.

                                                                        А что такого кардинального случилось, что он стал безопаснее? Все решения носят рекомендательный характер. "Пожалуйста, не пользуйтесь сырыми указателями", "Пожалуйста, не вызывайте delete руками", "Пожалуйста, избегайте утечек", "Пожалуйста, не вызывайте operator-> у пустого optional". Слишком много "пожалуйста", и слишком дорогая цена за промашку.

                                                                          +1
                                                                          Особенно вот этот несложный гайдлайн мне очень нравится.
                                                                          — Пожалуйста, избегайте гонок данных.
                                                                          — Да, мой капитан!

                                                                          ну от этого и раст не застрахован

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

                                                                          1. Мув-семантика. Без неё даже unique_ptr нельзя было написать. Теперь не надо выбирать между COW, сырыми указателями и постоянным копированием, то есть не нужны компромиссы между безопасным владением и быстродействием.

                                                                          2. Синтаксический сахар (auto, лямбды, range-based for, ...). Меньше кода => меньше ошибок, и этот эффект куда заметнее чем кажется. Пример: раньше народ сравнительно мало пользовался итераторами — очень много синтаксического шума, предпочитали писать на индексах, ибо «проще, понятнее, быстрее писать и красивее». Вот только индексы — рассадник ошибок. Сейчас код на тех же итераторах выглядит лаконичнее и проще индексов из-за auto и лямбд. А еще он надежнее.

                                                                          3. Расширения стандартной библиотеки. Представьте, что у вас миллионы программистов разом договорились как управлять памятью и передавать владение (unique_ptr&& выразительнее T*), как выразить nullable value (optional вместо T*), как эффективно передавать строки (string_view вместо const char *), как обойтись без гребаного синглтона,… Банально отпала необходимость писать/читать/дебажить/оборачивать целый пласт велосипедов.

                                                                          Собственно теперь все эти утилитарные вещи почти не требуют когнитивных ресурсов, и больше остается на решение задачи. Как раз то, за что вы так любите раст, верно?
                                                                            –1
                                                                            ну от этого и раст не застрахован

                                                                            Если под гонкой данных подразумевается "Data race", то Rust от этого предохраняет.


                                                                            Количество UB в коде на C++ зависит от усидчивости программиста, который постоянно должен проверять инварианты языка и держать 1000+ страниц стандарта и ещё 100 страниц рекомендаций. Каков шанс, что в крупном проекте он всё-таки ошибётся?

                                                                              –3
                                                                              Если под гонкой данных подразумевается «Data race», то Rust от этого предохраняет.

                                                                              Текст по ссылке: «However Rust does not prevent general race conditions.»

                                                                              Количество UB в коде на C++ зависит от усидчивости программиста, который постоянно должен проверять инварианты языка и держать 1000+ страниц стандарта и ещё 100 страниц рекомендаций

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

                                                                              Каков шанс, что в крупном проекте он всё-таки ошибётся?

                                                                              image
                                                                                0

                                                                                Перечитайте мой комментарий. Я говорил о UB в коде, не про логические ошибки.


                                                                                Текст по ссылке: «However Rust does not prevent general race conditions.»

                                                                                Речь о data races шла.


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

                                                                                Почему нет?

                                                                                  +2
                                                                                  Я говорил о UB в коде, не про логические ошибки.

                                                                                  в моей практике UB на 2-3 порядка реже логических ошибок. Это достаточно много, чтобы предпочесть жизнь в тирании компилятора?

                                                                                  Вот потому и преувеличение
                                                                                    +2
                                                                                    предпочесть жизнь в тирании компилятора?

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

                                                                                      +1
                                                                                      И еще многое из того, что не является. Например, просто попытку получить несколько mut ref на один объект. Я вон выше привел ссылку, как автор Way Cooler устал бороться с растовскими велосипедами, послал их нахрен, и переписал все на C. И это после того, как он потратил на борьбу с ними около трех лет.
                                                                                        +1

                                                                                        Судя по описанию, он пытался обернуть АПИ в стиле "рандомные дескрипторы живут рандомное время". Т.е. когда у каждого объекта есть по факту неконтроллируемое количество неконтроллируемых точек доступа, в т.ч. на запись. Такое естественно не обернуть в статическую модель.

                                                                                      +1
                                                                                      в моей практике UB на 2-3 порядка реже логических ошибок. Это достаточно много, чтобы предпочесть жизнь в тирании компилятора?

                                                                                      А как Вы поняли, что UB в Ваших программах отсутствует?

                                                                                        –1
                                                                                        А как вы, растовики, это понимаете? Вы просто веруете в то, что в ансейф-обертках, которые лепятся вокруг сисколлов, сторонних библиотек, а также в stdlib (а без них никак), UB нет, вот и все. Да и стабильность работы этих оберток под боольшим вопросом — например, когда собираешь под 64 бита, UB нет, а когда под 32 бита — то есть. Не стоит лепить из раста серебряную пулю, которая сама по себе гарантирует отсутствие UB, это просто смешно.
                                                                                          +5

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

                                                                                            0
                                                                                            в моей практике UB на 2-3 порядка реже логических ошибок.

                                                                                            Вот и мне интересно каким же образом человек понял что в его программах UB отсутствует.


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

                                                                                            Rust не будет первопричиной UB либо если код без Unsafe блоков, либо Unsafe блоки не содержат UB. Если кто-то допустил UB в Unsafe блоке — это его вина, таким же образом можно допустить UB в операционной системе/в драйверах/в железе, просто Rust предоставляет удобные инструменты чтобы UB обнаружить. Не является ли C/C++ первопричиной UB можно только глазами проверять, никак иначе.

                                                                                      +1
                                                                                      Текст по ссылке: «However Rust does not prevent general race conditions.»

                                                                                      А чуть ниже не прочли? Это касается случаев, когда бага в ОС или железе.

                                                                                        +2
                                                                                        General race conditions — это не «бага в ОС или железе». Это, например, типичная ошибка новичка — сначала проверить, что файла нет, затем его создать. Или сначала проверить, что записи в таблице нет, а затем ее добавить. Rust защищает от одной-единственной гонки — изменение содержимого некоего адреса в RAM из нескольких потоков одновременно. Больше ни от чего. А видов race conditions еще вагон и маленькая тележка. Впрочем, это тут уже обсуждалось в рамках другого треда — и «защита» раста, и цена, которую ты за нее платишь. Разговор пошел по кругу.
                                                                                          0
                                                                                          А чуть ниже не прочли? Это касается случаев, когда бага в ОС или железе.

                                                                                          а еще чуть ниже? «So it's perfectly „fine“ for a Safe Rust program to get deadlocked or do something nonsensical with incorrect synchronization»
                                                                                      +5
                                                                                      ну от этого и раст не застрахован

                                                                                      Застрахован.


                                                                                      1. Мув-семантика… между безопасным владением и быстродействием

                                                                                      А Шон Пэрент считает иначе. Ситуацию бы спас destructive move, которого пока нет. А пока что это мув-семантика существует только для скорости. Вот таким нехитрым образом можно вызвать ub с помощью "безопасного" move:


                                                                                      std::vector<std::string> v;
                                                                                      std::string str = "example";
                                                                                      v.push_back(std::move(str)); // str is now valid but unspecified
                                                                                      str.back(); // undefined behavior if size() == 0: back() has a precondition !empty()

                                                                                      1. Синтаксический сахар…

                                                                                      Да, я согласен. Меньше кода => меньше ошибок. Тем не менее гарантии Раста предоставляются не сахаром, а системой типов. Раст во время компиляции определяет, что незащищенная(мьютексом) переменная летит в другой тред и выдает ошибку компиляции (не варнинг, нельзя отключить флагами, нельзя обойти блоком unsafe). Так же, как const в С++ запрещает менять значение переменной. Это все к спору застрахован — не застрахован.


                                                                                      1. Расширения стандартной библиотеки… выразить nullable value (optional вместо T*)

                                                                                      Это все здорово, но зачем расширения стандартной библиотеки добавляют все больше и больше UB? operator-> на пустом optional есть неопределенное поведение. Это противоречит вашему заявлению, что С++ становится безопаснее. Наоборот, теперь можно еще одним дополнительным способом выстрелить в ногу.


                                                                                      Как раз то, за что вы так любите раст, верно?

                                                                                      Лично я люблю Раст за безопасность, не за сахар.

                                                                                        0
                                                                                        Лично я люблю Раст за безопасность, не за сахар.

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

                                                                                        Неужели вы думаете, что Option::expect концептуально лучше optional::operator-> только потому, что там пишется какое-то сообщение? Приложение-то всё равно упало.
                                                                                          +6
                                                                                          Не нужно быть гением чтобы ...

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


                                                                                          Неужели вы думаете, что Option::expect концептуально лучше optional::operator-> только потому, что там пишется какое-то сообщение? Приложение-то всё равно упало.

                                                                                          Конечно) Достать данные из растового Option в обход системы типов у вас не получится. Option::expect — это один из видов проверки, когда приложение завершает свою работу, если внутри пусто. Вы можете добавить свою проверку по вкусу. Без падений. Но UB все равно не будет.


                                                                                          Ну и смею напомнить, что UB != падение приложения, это может быть все, что угодно. Код может делать rm -rf /, может запускать Ханойские башни, может упасть (самый счастливый случай), может зависнуть, может обнулить показания высоты и заставить самолет считать, что он находится на земле.

                                                                                            –1
                                                                                            Вы говорите, что новый С++ стал лучше, но опять выходит, что ему добавили сахара, а за безопасностью все равно надо следить самому.

                                                                                            Как говорил капитан Алатристе — «для кого… лучше»? Бывает, что и лучше последить за безопасностью самому, и проверить optional.has_value() где-то повыше, чем иметь эту проверку в неявном автоматическом виде где-нибудь на пятом уровне нагруженного цикла при разыменовании optional через "*" или "->". Для любителей таких проверок на каждый чих есть Java или C#. C++ не для вас.
                                                                                              +1
                                                                                              Как говорил капитан Алатристе — «для кого… лучше»

                                                                                              Окунитесь в тред, прежде чем спрашивать. https://habr.com/en/post/467901/#comment_20667479


                                                                                              Бывает, что и лучше последить за безопасностью самому, и проверить optional.has_value() где-то повыше

                                                                                              Конечно. Бывает и лучше. Проверили один раз и носите валидное значение. Мы говорим про случай, когда из optional значение достали, а проверить забыли.

                                                                                                0
                                                                                                Вы, по-моему, не очень поняли, о чем я. Перечитайте мой комментарий еще раз. В std::optional есть value(), которая делает проверку, и есть operator * и operator ->, которые ее не делают. Это сделано специально, для случаев, когда такая проверка только замедляет — а именно когда проверка уже сделана где-то выше разработчиком, и он хочет избежать неявных проверок при дальнейших обращениях. Вот здесь в Notes даже специально написано, почему так:
                                                                                                The dereference operator operator*() does not check if this optional contains a value, which may be more efficient than value().
                                                                                                  0

                                                                                                  Я понял, о чем вы. Я отвечал на вопрос, чем Option::expect концептуально лучше. В Раст значение из Option можно достать в одно движение(и проверка, и распаковка), в С++ можно ошибиться и использовать такой удобный и приятный *opt.

                                                                                                    0
                                                                                                    В std::optional тоже можно достать в одно движение (и проверка, и распаковка) — через value(). Но можно и без проверки — для случаев, когда место критичное в плане скорости, а проверка уже сделана выше. Есть выбор. В расте выбора нет — только рантайм проверки на каждый чих, только хардкор.
                                                                                                      +1
                                                                                                      В расте выбора нет.

                                                                                                      В расте невозможна такая ситуация из-за destructive move)) Проверил, достал значение — потерял исходный Option. Всё. Одна проверка гарантируется во время компиляции. Быстро и безопасно. Почему вы считаете это недостатком?)))

                                                                                                        0
                                                                                                        Потому, что значение нужно доставать и куда-то класть. Возможно, это действие может быть оптимизировано… а возможно, и нет.
                                                                                                          0
                                                                                                          fn main() {
                                                                                                              let s = "hello".to_string();
                                                                                                              let opt_s = Some(s);
                                                                                                              let v = opt_s.unwrap_or_default();
                                                                                                              dbg!(v);
                                                                                                              // let v_second_time = opt_s.unwrap_or_default();
                                                                                                          }

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


                                                                                                          Если раскомментируете строчку с v_second_time, получите ошибку


                                                                                                          error[E0382]: use of moved value: `opt_s`
                                                                                                           --> src/main.rs:7:25
                                                                                                            |
                                                                                                          4 |     let opt_s = Some(s);
                                                                                                            |         ----- move occurs because `opt_s` has type `std::option::Option<std::string::String>`, which does not implement the `Copy` trait
                                                                                                          5 |     let v = opt_s.unwrap_or_default();
                                                                                                            |             ----- value moved here
                                                                                                          6 |     dbg!(v);
                                                                                                          7 |     let v_second_time = opt_s.unwrap_or_default();
                                                                                                            |                         ^^^^^ value used here after move

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

                                                                                                          Какая разница? Мы говорим о безопасности, которую предоставляет Rust даже в таких мелочах как Option и повторная распаковка, или распаковка без проверки.

                                                                                                            0
                                                                                                            Какая разница? Мы говорим о безопасности

                                                                                                            Прелестно. Ладно, не интересно.
                                                                                                            +1

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

                                                                                                          0

                                                                                                          Редко, где узкое место в производительности — это методы Option::expect/Option::unwrap. Если сильно надо, то можно так попробовать:


                                                                                                          #![feature(core_intrinsics)]
                                                                                                          
                                                                                                          pub fn extract(value: Option<i32>) -> i32 {
                                                                                                              unsafe { std::intrinsics::assume(value != None); }
                                                                                                              value.unwrap()
                                                                                                          }

                                                                                                          Оптимизируется на ура: https://rust.godbolt.org/z/VzsdA0.

                                                                                                            +2

                                                                                                            Только не должна ли эта функция сама быть unsafe?

                                                                                                              –1

                                                                                                              Должна, ошибся немного)

                                                                                                              +2

                                                                                                              Можно и без нестабильных фич: https://rust.godbolt.org/z/e-OrLW


                                                                                                              Нужен UB для оптимизации? Явно добавляем UB.

                                                                                                      +1
                                                                                                      Бывает, что и лучше последить за безопасностью самому, и проверить optional.has_value() где-то повыше, чем иметь эту проверку в неявном автоматическом виде где-нибудь на пятом уровне нагруженного цикла при разыменовании optional через "*" или "->".

                                                                                                      Это вы на зависимые типы напрашиваетесь.

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

                                                                                                      но делать это теперь на порядки проще. Я не спорю, плюсы небезопасны. Но на них достаточно просто писать достаточно безопасный для любого практического применения код.
                                                                                                        +2
                                                                                                        Но на них достаточно просто писать достаточно безопасный для любого практического применения код.

                                                                                                        А вот с этим утверждением я не согласен.

                                                                                                          –1
                                                                                                          вы же понимаете, что код на расте тоже не на 100% безопасен? Где вы проводите грань между достаточно/недостаточно безопасными технологиями? Потому что существует разница между аргументированным выбором (а-ля «в силу специфики проекта мне необходим определенный уровень надежности софта»*) и откровенным фанатизмом («всё не такое безопасное как раст — говно»**), и если ваш случай — второй, то я не хочу продолжать этот безнадежный спор

                                                                                                          * Подразумевается, что в этом случае выбор ЯП — не единственное мероприятие, направленное на повышение надежности.
                                                                                                          ** В таком случае причина замыкается на следствие
                                                                                                            +1

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

                                                                                                              0
                                                                                                              Я считаю, что на расте можно «просто писать достаточно безопасный для любого практического применения код», а на плюсах — нет.

                                                                                                              Где вы проводите грань между достаточно/недостаточно безопасными технологиями?
                                                                                                                0

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

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

                                                                                                                  я думаю средний плюсовик с годом опыта так тоже может, если он начинал с с++17 и Майерса.
                                                                                                                    +1

                                                                                                                    Ну вот а я так не думаю :)

                                                                                                                0
                                                                                                                просто писать

                                                                                                                Ну видимо это ключевое. То есть писать не понимая дальше того, что у тебя сейчас высвечивается на экране.
                                                                                                                Размен вырабатонной годами практики понимания того, что же на самом будет выполняться на CPU (с некоторым уровнем условности), на оверхед ублажения компилятора.
                                                                                                                У человека, только иногда что-то пишущего на C++, результат на расте будет лучше.
                                                                                      +2

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


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


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

                                                                                        0

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

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

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


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

                                                                                              +1

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

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

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


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

                                                                                                    0

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


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

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

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

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

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

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

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

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

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

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

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


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


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

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

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

                                                                                                                          0
                                                                                                                          Такое религиозное рвение, конечно, похвально, но, как кто-то сказал про тот же 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 :)
                                                                                                                            0

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

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

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

                                                                                                                                  0

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


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


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

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

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

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

                                                                                                                                        +2

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

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

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


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

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


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

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

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

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

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

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

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

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

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


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

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

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


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

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

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

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


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

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

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

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

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

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


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

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

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

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

                                                                                                                                                        :)
                                                                                                                                                          0

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

                                                                                                                                    0

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

                                                                                                                                      0

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

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

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


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

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


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


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

                                                                                                                                        0

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

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

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

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

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

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

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

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

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

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

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

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

                                                                                                                                    0

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

                                                                                                                                      0

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

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