Так ли страшен Rust, как его малюют

    Некоторое время назад я начал понимать необходимость разнообразить мой опыт программирования исключительно на C#. После некоторого изучения различных вариантов, таких как Haskell, Scala, Rust и некоторых других, выбор пал на последний. Со временем я начал обращать внимание, что Rust всё больше и больше рекламируется исключительно как "системный язык", который нужен для вырвиглазно сложных компиляторов и супер-нагруженных систем, с особыми требованиями к безопасности и многопоточности, а для вариантов попроще есть Go/Python/Java/..., в то время как я с удовольствием и весьма успешно использовал его как замену моей рабочей лошадке C#.



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


    Введение


    Зачем, собственно, учить новый язык, тем более сложный? Мне кажется, что ближе всего к истине ответ статьи "Побеждая посредственность", а именно:


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

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


    Основы


    Как обычно новичок знакомится с языком программирования? Он гуглит самую популярную книжку по языку, достаёт её, и начинает читать. Как правило, там содержится HelloWorld, инструкция по установке компилятора, а дальше базовая информация по языку с постепенным усложнением. В случае раста, это растбук, а первым примером является чтение числа из консоли и вывод его на экран. Как бы мы это сделали в том же C#? Ну наверное как-то так


    var number = int.Parse(Console.ReadLine());
    Console.WriteLine($"You guessed: {number}");

    А что у нас в расте?


    let mut guess = String::new();
    
    io::stdin().read_line(&mut guess)
        .expect("Failed to read line");
    
    let guess: u32 = guess.trim().parse()
        .expect("Please type a number!");
    
    println!("You guessed: {}", guess);

    Утроенное количество кода, все эти реверансы с созданием переменной перед использованием (привет Паскаль!), вызовом кучи вспомогательного кода, и т.п. "Что за ужас" подумает среднестатистический разработчик и в очередной раз убедится в "системности" языка.


    А ведь на самом деле это можно написать существенно проще:


    let mut guess = String::new();
    io::stdin().read_line(&mut guess)?;
    let guess: u32 = guess.trim().parse()?;
    println!("You guessed: {}", guess);

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


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


    Лайфтаймы и борроучекер


    Ох уж эти страшные звери. Люди бросаются непонятными заклинаниями навроде


    fn search<F>(self, hash: u64, is_match: F, compare_hashes: bool)  
       -> RawEntryMut<'a, K, V, S>
      where for<'b> F: FnMut(&'b K) -> bool

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


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


    • Each elided lifetime in input position becomes a distinct lifetime parameter.
    • If there is exactly one input lifetime position (elided or not), that lifetime is assigned to all elided output lifetimes.
    • If there are multiple input lifetime positions, but one of them is &self or &mut self, the lifetime of self is assigned to all elided output lifetimes.
    • Otherwise, it is an error to elide an output lifetime.

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


    struct Point(i32, i32);
    
    impl Point {
        pub fn get_x(&self) -> &i32 {
            &self.0
        }
    
        pub fn get_y(&self) -> &i32 {
            &self.1
        }
    }

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


    Лично я вижу прелесть концепции в автоматическом управлении в нескольких аспектах


    • с точки зрения человека с опытом программирования на языке с GC память не является отдельным видов ресурсов. В C# есть целая история с интерфейсом IDisposable, который используется для детерминированной очистки ресурсов, именно потому, что GC удаляет объект "когда то там", а нам может потребоваться освободить ресурс немедленно. В итоге есть целый ворох следствий: и про правильную реализацию финализаторов надо не забыть, и целое ключевое слово для этого было введено (как и try-with-resources в Java), и компилятор перелопатить, чтобы генерировал foreach с учетом этого… Унификация всех видов ресурсов, которые освободятся автоматически, и максимально быстро после последнего использования это очень приятно. Открыл себе файл, и работаешь с ним, он закроется когда нужно без всяких скоупингов. Сразу отвечу на потенциальное возражение, что DI контейнеры несколько облегчают жизнь, но не решают всех вопросов
    • с точки зрения человека с опытом программирования на языке с ручным управлением, в 99% случаев не надо использовать умные указатели, достаточно использовать обычные ссылки.

    В итоге, код получается чистый (как в языке с GC), но в то же время все ресурсы освобождаются максимально быстро (как в языке с ручным управлением). А лайфтайм: декларативное описание ожидаемого времени жизни объекта. А декларативное описание всегда лучше, чем императивное "освободи объект здесь".


    Жестокий компилятор


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



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


    pub struct Node {
        value: u64,
        next: Option<Box<Node>>,
        prev: Option<Box<Node>>,
    }

    Компилятор скомпилирует объявление этой структуры, но воспользоваться ей не получится, т.к. Box<Node> является владеющей ссылкой, или unique_ptr в терминах C++. А уникальная ссылка, конечно же, может быть только одна


    Следующая попытка человека может выглядеть так:


    pub struct Node {
        value: u64,
        next: Option<&Box<Node>>,
        prev: Option<&Box<Node>>,
    }

    Теперь у нас есть невладеющие ссылки (они же shared_ptr), и их может быть сколько угодно на один объект. Но тут возникает две проблемы: во-первых владелец должен где-то быть. А значит мы скорее всего получим кучу ошибок компиляции "владелец умер, когда кто-то ссылался на его данные", потому как dangling pointers раст не допускает. А во-вторых, что важнее, мы не сможем изменять эти значения, из-за правил раста "либо одна мутабельная ссылка, либо произвольное количество иммутабельных, и никак иначе".


    После этого человек обычно начинает биться о клавиатуру, и писать статьи что "в расте даже связный список реализовать нормально не получится". Реализовать же его, конечно, можно, но немного сложнее чем в других языках, придется руками добавить подсчёт ссылок (примитивы Rc/Arc/Cell/RefCell), чтобы рантайме подсчитывать количество этих самых ссылок, потому что компилятор в данной ситуации бессилен.


    Причины этого: эта структура данных плохо ложится на концепцию владения раста, вокруг построен весь язык и экосистема в целом. Любые структуры данных, где необходимо наличие нескольких владельцев потребуют некоторых приседаний, например реализация всевозможных лесов/деревьев/графов или тех же связных списков. Но это верно для всех языков программирования: попытки реализовать своё управление памятью в языках с GC приводит к страшным монстрам, работающих через WeakReferences с гигантскими byte[] массивам, воскрешающие объекты в деструкторах, чтобы вернуть их в пул, и прочей страшной некромантией. Попытки уйти от динамической природы JS, чтобы написать производительный код, приводит к еще более странным вещам.


    Таким образом, в любом языке программирования есть своя "болевая точка", и в случае раста, это структуры данных с многими владельцами. Но, если мы смотрим с прикладной точки зрения высокоуровневых программистов, наши программы устроенны как раз таким образом. Например, в моем окружении типовое приложение выглядит как некоторый слой контроллеров, которые шарят между собой сервисы. Каждый сервис имеет ссылку на какие-то репозитории, которые возвращают какие-то объекты. Всё это отлично укладывается в концепцию ownership'а. И если учесть, что на практике основными структурами данных являются списки, массивы и хэшмапы, то оказывается, что всё не так уж и плохо.


    Что же делать с этим зверем


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


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


    error[E0382]: assign to part of moved value: `head`
      --> src\main.rs:23:5
       |
    19 |         prev: Some(Box::new(head)),
       |                             ---- value moved here
    ...
    23 |     head.next = Some(Box::new(next));
       |     ^^^^^^^^^ value partially assigned here after move
       |
       = note: move occurs because `head` has type `Node`, which does not implement the `Copy` trait

    Он говорит как раз о том, что мы передали владение ссылкой одному элементу, и уже не можем его использовать повторно. Также он нам рассказывает, что есть некий Copy трейт, который позволяет вместо перемещения объекта производить его копирование, из-за чего его использовать после "перемещения", потому что переместили мы копию. Если вы не знали про его существование, то ошибка компиляции снабдит вас информацией для размышления "А может стоит добавить реализацию этого трейта?".


    Вообще, раст для меня первый язык, в котором есть compiler-driven development. Вы просто запускаете компиляцию, если что-то не работает, язык просто скажет вам "хмм, что-то не сходится. Я думаю, проблема в Х. Попробуй добавить вот этот код, и всё заработает". Типовой пример, допустим мы написали две функции, и забыли добавить ограничение на генерик:


    fn foo<T: Copy>() {
    
    }
    
    fn bar<T>() {
        foo::<T>();
    }

    Компилируем, получаем ошибку:


    error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied
     --> src\main.rs:6:5
      |
    6 |     foo::<T>();
      |     ^^^^^^^^ the trait `std::marker::Copy` is not implemented for `T`
      |
      = help: consider adding a `where T: std::marker::Copy` bound
    note: required by `foo`
     --> src\main.rs:1:1
      |
    1 | fn foo<T: Copy>() {
      | ^^^^^^^^^^^^^^^^^
    
    error: aborting due to previous error

    Копипастим where T: std::marker::Copy из сообщения об ошибке, компилируем, всё готово, поехали в прод!


    Да, IDE всех современных языков умеют это делать через всякие сниппеты, но во-первых тут польза в том, что вы видите, из какого крейта/неймспейса прилетело ограничение, а во-вторых это поддержка всё же со стороны компилятора, а не IDE. Это очень помогает при кросс-платформенной разработке, когда у вас локально всё собирается, а на некоторой матрице на CI сервере где-то что-то падает из-за условной компиляции. На билд-сервере IDE нет, а так лог глянул, подставил, и всё собралось. Удобно.


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


    Ну и могу сказать, что по прошествии года с того момента как я впервые начал на расте писать, я научился писать простые сниппеты без ошибок с первого раза. Звучит смешно, особенно для людей с динамических ЯП, но для меня это был серьезный прогресс. А еще за всё время работы с растом я дебаг включал ровно два раза. И в обоих случаях я дебажил FFI с С++ кодом, который сегфолтился. Растовый код у меня либо работал правильно, либо не собирался. В случае с C# у меня уверенность сильно ниже, я все время думаю "а не придет ли тут null", "а не будет ли тут KeyNotFoundException", "правильно ли я синхронизировал доступ к этим переменным из многих потоков", и т.п. Ну а в случае с JS (когда я фуллстечил и писал фронт в том числе) после каждого изменения следовала обязательная проверка в браузере, что там изменилось.


    Уверенность в том, что собралось == работает действительно имеет место. Это не значит, что в коде нет багов, это значит, что все баги связаны с логикой приложения. У вас нет неожиданных нуллов, несихнронизированного доступа, buffer overflow и так далее. А их намного легче отловить, а иногда можно вынести на уровень типов (хорошая статья на тему).


    Итого


    Раст — отличный язык для написания абсолютно любых приложений, а не только высоконагруженных бирж, блокчейнов и трейдинговых ботов. Всегда вместо передачи ссылки можно просто скопировать значение. Да-да, возможно, растовчане закидают меня камнями, но в в паре мест моего бота я вместо того, чтобы силиться объяснить компилятору, что переменную можно спокойно расшарить, я её клонировал, и передавал копию. Да, это не так классно, но у меня нет цели написать максимально производительное приложение, как нет такой цели у людей, пользующихся C#/Java/Go/… Я хочу быть максимально продуктивным, и получить приложение с приемлемой скоростью. Реализовать приложение на расте по всем канонам, исключив все ненужные копирования — весьма непростая задача. Но написать приложение за то же время, что и на своём любимом языке, и получить еще и бесплатный прирост производительности — очень даже реально.


    Попробуйте написать приложение на расте. Если у вас не получается пройти борроучекер, проверьте ваши структуры данных и их взаимосвязи, потому что я постепенно начал понимать, что борроучекер это не просто механизм, отвечающий за возможность освобождения памяти, но и отличный детектор правильности архитектуры приложения, из разряда "хей, а почему это объект Х зависит от У, я этого не предполагал!". Если же вы всё понимаете, но объяснять борроучекеру правильный ответ слишком сложно, просто скопируйте значение. Скорее всего, вы все равно получите приложение, работающее намного быстрее (если вы пишете на Java/C#/..., как я), либо намного стабильнее (если вы пишете на С/С++), за то же самое время, которое вы бы обычно затратили.


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

    Поделиться публикацией

    Похожие публикации

    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

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

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

      –4
      Нормально так набросили.
      собралось == работает
      для написания абсолютно любых приложений
      которые не задумываются о производительности
      Признайтесь, вы специально? )
        +22

        Не знаю, в чем тут наброс. Я действительно считаю, что раст форсится исключительно как "Better C", который "такой же быстрый, но безопасный". И совершенно игнорируют тот факт, что это хороший язык общего назначения, на котором и микросервисы серверлесные можно делать, и другие интересные штуки. Все знают, что крутую распределенную систему можно быстро и удобно сделать на Akka, а вот что есть точно такой же фреймворк для раста (actix), в котором точно так же удобно можно всё сделать на расте — уже нет.


        Я правда считаю, что раст продуктивный, и что его производительность — это хорошая, но далеко не единственная черта.И во многих случаях ей можно пожертвовать, получим очень простой и изящный код. В случае с тем же ботом у меня ~3-4 сетевых запроса на каждый пост пользователя: сначала обработка самого сообщения, затем запрос урлов файлов, затем запрос контента файлов, и потом еще и отправка сообщения в телеграм. Сколько относительно этого ест клонирование единственной строковой переменной?


        С появлением async/await (уже есть в ночнике, я бота его недавно на него переписал) должна отойти самая насущная на сегодняшний день проблема, неудобный async IO на коллбеках. А других серьезных проблем, способных помешать продуктивно писать код, я не вижу. После некоторой практики на расте можно писать с той же скоростью, что и на C#, но получать намного более серьезные гарантии корректности. Да, эту практику нужно набить, но как я уже сказал в статье, "тяжело в учении — легко в бою", вы учите концепцию один раз, и дальше это как езда на велосипеде, всегда с вами до конца жизни. Затратили сколько-то времени, дальше этим пользуетесь, и чем раньше изучили, тем больше времени сэкономили.

          +1
          А как у Раста с ORM-ами ну и вообще с поддержкой БД? Можете что-нибудь посоветовать?
          Сам давно хотел попробовать что-нибудь кроме .NET на бекэнде. Но после EntityFramework как-то плеваться начинаешь на ORM-ы в других языках.
            +3
            Есть Diesel, правда список поддерживаемых БД скромный — SQLite, PostgreSQL, и MySQL.
              0
              Удалено.
              +2
              Дела с ORM: неплохо, но до EF конечно же пока не дотягивает. Кое-что есть примерно на уровне Linq2Sql. Классическая связка: r2d2 + diesel. Умеет в генерацию типов по схеме + LINQ-подобный dsl. Лично я проверял работу с постгресом, но есть разные провайдеры. Раньше не умел в миграции, сейчас в репе я что-то вижу на эту тему, но не пробовал.

              Прям уровень EF это пока рановато, не зря он уже по сути восьмой версии (6 версий взрослого фреймворка, и две на Core). Но в целом, работать с БД можно.
                +1

                Спасибо, почитал! Я так понял, что концепта UnitOfWork там нет?
                Хотя вот это — огонь :)


                // Using `include_str!` allows us to keep the SQL in a
                // separate file, where our editor can give us SQL specific
                // syntax highlighting.
                sql_query(include_str!("complex_users_by_organization.sql"))

                А аналог nameof() в Расте есть?

                  0

                  Есть макрос stringify!(), превращающий токены в строку. И есть крейт nameof на его основе, делающий примерно то же, что и nameof().

                    +1
                    Спасибо, почитал! Я так понял, что концепта UnitOfWork там нет?

                    Ну вроде как let connection = pool.get(); это оно, нет?


                    А аналог nameof() в Расте есть?

                    нет, но мы же про раст говорим :) 2 секунды, и уже есть :)


                    macro_rules! name_of {
                        ($x:ident) => {
                            stringify!($x)
                        }
                    }
                    
                    fn main() {
                        let my_variable = 10;
                        let name = name_of!(my_variable);
                        println!("{}", name);
                    }

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

                      0
                      let connection = pool.get(); это оно, нет?

                      Я имею в виду аналог DbContext:


                      post.published = true;
                      post.save_changes(&connection);

                      Если я правильно понимаю, save_changes(&connection) сохраняет прямо в базу. А группировка изменений по нескольким сущностям обеспечивается явной транзакцией. В противовес DbContext.saveChanges() из EF, который группирует изменения в один запрос.


                      name_of!(my_variable);

                      А name_of(Post::published) можно? В .NET я это использую для того, чтобы RAW SQL не требовал изменений после рефакторинга названий классов и полей.

                        0
                        А, ну группировки в одну транзакцию действительно я не видел. Хотя возможно уже добавили. Не берусь сказать.

                        А name_of(Post::published) можно? В .NET я это использую для того, чтобы RAW SQL не требовал изменений после рефакторинга названий классов и полей.

                        Ниже сказали, я немного неверно понимал как $ident работает. Нужно подумать еще.
                          +1
                          Ниже отредактировал ответ.

                          > А name_of(Post::published) можно?

                          можно всё, что является идентификатором. Можно посмотреть на реальный макрос реального крейта, там из комментов понятно, какие случаи работают, и даже как именно их обрабатывать: docs.rs/nameof/1.0.1/src/nameof/lib.rs.html#72-93
                          0

                          Увы эта реализация не похожа на nameof из C#


                          macro_rules! name_of {
                              ($x:ident) => {
                                  stringify!($x)
                              }
                          }
                          
                          fn main() {
                              let my_variable = 10;
                              let name = name_of!(my_variable_1);
                              println!("{}", name);
                          }

                          выведет в консоль


                          my_variable_1

                          https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=3ffad15450a43fabab337e37e22440aa
                          а C# код


                          using System;
                          
                          public class Program
                          {
                              public static void Main()
                              {
                                  var myVariable = 10;
                                  Console.WriteLine(nameof(myVariable1));
                              }
                          }

                          не скомпилируется с ошибкой:


                          Compilation error (line 8, col 28): The name 'myVariable1' does not exist in the current context
                          Compilation error (line 7, col 7): The variable 'myVariable' is assigned but its value is never used

                          https://dotnetfiddle.net/UHBtJh

                            +3

                            Да, вы правы. Я забыл, что $ident может не только использовать существующий идентификатор, но и новый объявлять. Беру паузу на размышление :)




                            Подглядел в крейт nameof, чтобы посмотреть, как они это сделали. Собственно, так же, как и я, только с гвардом, который как-то использует переменную: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=40fdb6ff604b9f094faaaaf40ee826cb

                              –18
                              Отлично. Как и в предыдущем флейме выяснилось, что растишки и сами не знают восхваляемого языка, да и ошибки он позволяет делать на ровном месте.

                              можно всё, что является идентификатором. Можно посмотреть на реальный макрос реального крейта, там из комментов понятно, какие случаи работают, и даже как именно их обрабатывать: docs.rs/nameof/1.0.1/src/nameof/lib.rs.html#72-93
                              Код великолепен — как древние С- макросы, только с привкусом брейнфака.
                                +3
                                От сишных «макросов» тут одно название. Это ближе к лиспу и работе с AST, а не тупая автозамена. Если с лиспом работали, то понимаете, насколько это мощная концепция. Мы буквально за пару минут реализовали фичу языка, которую в шарпах делали несколько месяцев, и которую еще несколько лет ждали в релизе. Как по мне, это успех.
                                  0
                                  Ошибки позволяет делать абсолютно любой язык. Просто некоторые в ответ отказываются компилировать код (или, напротив, компилируют то, что компилироваться, по задумке, не должно), а некоторые — доводят ошибку до рантайма и выдают какую-то ересь. Думаю, Вы не станете спорить, что Rust здесь себя проявил как язык из первой категории.
                                    –4
                                    Как раз из второй. Поскольку ошибка макроса дошла до выполнения.

                                    Макросы и трейты — зло. Макросы из трейтов — зло в квадрате.
                                      0
                                      Ну называйте их не макросами, а плагинами компилятора, если вам так проще. Вы вот LLVM пользуетесь? Там есть трансформаторы кода, оптимизаторы всякие. По сути достаточно близко. Это тоже зло?
                                        –4
                                        Текстовая подстановка — макросы, доступ к информации компилятора — трейты.

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

                                        И нет, писать на LLVM коде я не планирую — как и ассемблер — он небезопасен в использовании. Хороший язык должен максимально обходиться своими средствами, без всяких там asm/llvm вставок и трейтов тоже. А макросы просто устарели, типобезопасные шаблоны(генерики) — на две головы впереди.
                                          +2
                                          Ну «дыра» — это громко сказано. Из-за того, что может быть кто-то недопонял или забыл, что макросы работают на синтаксическом, а не семантическом уровне, сразу злорадствовать о дырах… такое себе. Естественно, от подобных логических ошибок никакой компилятор не защитит, и тесты писать нужно, хотя бы простейшие.
                                            –7
                                            Это не логическая ошибка. Это именно пц

                                            Надо жить без синтаксических макросов. 3й раз пишу, не доходит.
                                              0

                                              Кому надо? Зачем надо?

                                                0
                                                Не надо, но синтаксические макросы, которые тупо получают поток токенов — это так себе.

                                                Вот хорошо делать, как TH, когда всё уже распаршено и представлено.
                                                  0
                                                  По сути так и сделано, но это решено отдельными крейтами, в частности syn и quote. Из преимуществ, можно сделать более удобный крейт для парсинга, не трогая компилятор. В общем, проблем с этим нет, с сырым потоком токенов никто, конечно же, не работает.
                                                  –4
                                                  Всем надо. Граблеопасная техника.

                                                  Сейчас заканчиваю статью о надежном программировании — вот велкам там прогнать раст через прокрустово ложе всех требований (потому что я этого сделать все= не смогу).
                                                    0
                                                    Всем надо. Граблеопасная техника.

                                                    Так «в чем опасность-то»? Или это «очевидно» и в обосновании не нуждается?
                                                      –5
                                                      Т.е сообразить самостоятельно после сделанной ошибки не получилось?

                                                      Вообще то после некоторого опыта конечно очевидно. Ослабляет проверку типов.

                                                      Но еще есть и гугл и учебники. В частности, в Misra C 2004 правило 93 как рекомендация не использовать, и в Misra C++ 2008 правило 6-2-2 вообще запрещающее функциональные макросы.
                                                        +1
                                                        А при чем тут правила С? Я вам уже говорил, что «Макрос» в С и расте это две совершенно разные штуковины. Так же, как слово Лист в русском и List в английском означают немного разные вещи.
                                                          –3
                                                          А сосед-растишка — говорил обратное. Поскольку я не знаю, кому верить, закругляюсь.

                                                          Но поскольку ошибка в наличии, она таки требует объяснений.
                                                            0
                                                            А сосед-растишка — говорил обратное. Поскольку я не знаю, кому верить, закругляюсь.

                                                            Там ничего не написано про "макросы в расте такие же как в С". И я предложил бы вам вежливее отзываться о ваших коллегах-программистах.


                                                            Но поскольку ошибка в наличии, она таки требует объяснений.

                                                            Ошибка в том, что я забыл, что $ident должен являться валидным идентификатором. Но в момент вызова макроса мы этот идентификатор и создаем. Этим можно воспользоваться, например, для генерации бойлерплейт-кода:


                                                            macro_rules! construct_uint {
                                                                ($name:ident, $n_words:tt) => (
                                                                    #[repr(C)]
                                                                    #[derive(Copy, Clone, Eq, PartialEq, Hash)]
                                                                    pub struct $name(pub [u64; $n_words]);
                                                            
                                                                    impl $name {
                                                                        pub const MAX: $name = $name([u64::max_value(); $n_words]);
                                                                    }
                                                                )
                                                            }
                                                            
                                                            construct_uint!(U128, 2);
                                                            construct_uint!(U256, 4);
                                                            construct_uint!(U512, 8);

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


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


                                                            Насчет дыры, это не большая дыра, чем такой код


                                                            fn add(a: i32, b: i32) -> i32 { a - b }

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

                                                              –2
                                                              А чем не нравится «растишка» (без негатива)?
                                                              Я честно перебрал термины — пишет на С — сишник, на паскале — паскалист, на расте = ??? растер, растишник. Готов к предложениям.

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

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

                                                              Потом еще глаз зацепился за unsafe в Особо Опасной функции вывода в строку.

                                                              На этом мои изыскания по расту окончены. Спс за общение.

                                                              P.S Код по ссылке выше, — можно вполне брать за образец и переписывать под свой язык — написан симпатично — это библиотечка работы с целыми числами любой разрядности
                                                                +3
                                                                А чем не нравится «растишка» (без негатива)?
                                                                Я честно перебрал термины — пишет на С — сишник, на паскале — паскалист, на расте = ??? растер, растишник. Готов к предложениям.

                                                                Английский вариант — rustacean, русский, видимо, растовчанин.


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

                                                                Можно подробьнее, в чем копипаст.


                                                                Потом еще глаз зацепился за unsafe в Особо Опасной функции вывода в строку.

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


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

                                                                  +2
                                                                  -ишк
                                                                  Словообразовательная единица (суффикс)
                                                                  1. под ударением при добавлении к основе существительного образует существительное со значением пренебрежительности
                                                                  https://ru.wiktionary.org/wiki/-ишк
                                                                    +1
                                                                    Английский вариант — rustacean, русский, видимо, растовчанин.

                                                                    Растовщик, растаман.
                                                +4
                                                Текстовая подстановка — макросы

                                                Макросы не занимаются текстовой подстановкой, если только это не С.

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

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

                                                И нет, писать на LLVM коде я не планирую — как и ассемблер — он небезопасен в использовании. Хороший язык должен максимально обходиться своими средствами, без всяких там asm/llvm вставок и трейтов тоже.

                                                То есть все языки, собирающиеся в LLVM стали дырявыми?

                                                А макросы просто устарели, типобезопасные шаблоны(генерики) — на две головы впереди.

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

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

                                                    А можно увидеть это заявление? А то пока походит больше на strawmen аргументацию.


                                                    Остальные пункты вы, видимо, решили проигнорировать.

                                                      –3
                                                      можно. например
                                                      Уверенность в том, что собралось == работает действительно имеет место. Это не значит, что в коде нет багов, это значит, что все баги связаны с логикой приложения.
                                                      не считая комментариев
                                                      это что касается успешной компиляции с несуществующей переменной

                                                      про остальное я не вижу конкретных вопросов. про макросы — ваш сочувствующий съехал на «синтаксическую составляющую», например, и что это нормально :fail:

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

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

                                                          –3
                                                          Как я и говорил — Растом не понимаю.

                                                          Но тебе большой вопрос — понимаешь ли ты разницу и можешь ли сказать однозначно — макросы в расте это просто синтаксическая подстановка (как заявлялось ранее) или компилируемый шаблон (который с проверкой типов)?

                                                          Потому что своим недопониманием (или недостаточным скиллом объяснений) ты подставляешь всё раст-сообщество как класс.
                                                            0

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


                                                            macro_rules! foo {
                                                                ($($t:tt)*) => {};
                                                            }
                                                            
                                                            fn main() {
                                                                foo!(Fuck your semantic!);
                                                            }

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

                                          +3
                                          ИМХО, как раз таки макросы это крутая фича как в том же Clojure
                              +1
                              И совершенно игнорируют тот факт, что это хороший язык общего назначения, на котором и микросервисы серверлесные можно делать, и другие интересные штуки

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


                              Я вот недавно писал мелкий сервис который болтает по GRPC, конвертит картинки в jpg/webp, кропает, и заливает в google cloud storage. Три дня на го.


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

                                0

                                С этим трудно поспорить. Я полгода потратил на написание cv-rs (биниднги к opencv), чтобы мой бот мог картинки анализировать. Было бы намного проще, если бы это кто-то сделал до меня.


                                Но для обычного приложения в стиле MVC с DAL слоем уже всё есть. Для специфики могут быть свои нюансы.

                                  +1
                                  Мало библиотек — это, конечно, плохо. Но с другой стороны — у вас есть много возможностей, чтобы создать свою библиотеку или стать мейнтейнером существующей, принести пользу сообществу и прославиться :)
                                    0

                                    grpc, webp, google cloud
                                    Ну было бы странно, если бы на go не было всё готовое для использования этих технологий.

                                      0

                                      GRPC официально работает на C++, Java, Python, Go, Ruby, C#, Node.js, Objective-C, PHP, Dart и в броузере.
                                      GCS API биндинги есть для C++, C#, Go, Java, Node.js, PHP, Python, Ruby.


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


                                      В rust есть набор костылей (объективно — тот же порт grpc не умеет несколько вещей которые мне нужны) и конструктор "сделай лего сам". Там где в PHP вы компонуете компоненты, хук, хук и впродакшен, на расте надо все еще писать обертки к API.


                                      Я не говорю что раст плохой, я говорю что у них (в отличие от того же Go/Python) "batteries are not included", а в cargo разброд и шатание.

                                    0
                                    Простите, а в каком месте actix «точно такой же фреймворк» как Akka?
                                    Там только акторы и всё. В акка есть кластер, стримы, http наконец и многое другое.
                                    Этот фреймворк далеко не просто акторы, в нем много библиотек выстраивающих экосистему.
                                    Все равно что сказать, Spring в Java это DI ))
                                      0
                                      Http в актикс есть, причем http2 автор добавил за пару недель, что ли, причем самостоятельно без помощи. Refactoring is a pleasure — действительно так.

                                      Кластеризация это да, мощная штука, но как уже говорил парень с дотнекста «Я вот щас вам рассказал про кластеры, но если есть возможность, НЕ ДЕЛАЙТЕ их» :)

                                      В данном случае, возможно, вы получите такой буст по производительности. что и кластер уже не нужен будет. И это снимет целую кучу потенциальных проблем.
                                  +12
                                  С душой написано, спасибо. Пожалуй, на праздниках попробую-таки раст.
                                    +4
                                    Да, я как С++-разработчик, получил удовольствие) Именно так и должны выглядеть пропагандистские статьи ;)
                                      +2
                                      Прямо озвучили мои мысли. Собрался на праздники в места далекие от интернетов и решил, раз уж такое дело, затариться литературой по расту.
                                      А автору — благодарность за труды.
                                      +5
                                      Rust страшен первые недели две. Потом только в удовольствие.
                                        +2

                                        Хотел этот вопрос задать в интернетах, но раз уж такая пляска, спрошу тут:


                                        Вчера писал калькулятор на расте. Суть в том, что можно написать "2 + 2", он это отпарсит и вернет ответ. Я сделал это по привычке на ООП: для выбора действия, я сделал трейт Operation с единственным методом run(i32, i32) -> i32. Сделал структуры с этим трейтом (самы структуры получились пустыми, в них нет состояния) и положил их в словарь в виде трейт объектов, которые дергаются по требованию.


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

                                          +5

                                          Ну, полагаю, rust-way более функциональный в данном случае. Там, где в ООП вы делаете абстрактный класс и кучу наследников, в ФП вы делаете один энум и матчите его в тех местах, где вам нужно. Где-то это дает выигрыш, где-то нет, это известная проблема выражения.. И для раста это выходит более естественно, чем прямой перенос ООП опыта. Я в серьезных крейтах трейт-объектов вообще не встречал, динамическая диспетчеризация используется очень редко.


                                          Вот пример крейта, построенного достаточно идеоматично: https://github.com/z2oh/sexe/blob/master/sexe-expression/src/lib.rs

                                            +1
                                            ФП эту проблему отлично решает через object algebras, кстати. На тайпклассах одно удовольствие потом с этим работать.

                                            А, ну ниже про tagless final написали.
                                              0
                                              Там, где в ООП вы делаете абстрактный класс и кучу наследников, в ФП вы делаете один энум и матчите его в тех местах, где вам нужно

                                              так это же по сути «смешать код в кучу» вместо инкапсуляции?
                                                +3

                                                Не совсем, просто подход иной.


                                                К слову, в ООП этот паттерн называется "Visitor", и используется в случаях известного множества классов чуть чаще, чем всегда. Поэтому если вы работали с AST, например, и писали VisitConstant/VisitBlock/VisitCondition/..., то это оно и есть.

                                                  0
                                                  а если множество классов заранее неизвестно?
                                                    0
                                                    тогда tagless final. Точно так же, как в ООП есть паттерн Visitor для статических иерархий, в ФП есть паттерн для динамических.
                                              +3

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


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

                                                +7

                                                У вашего варианта есть фатальный недостаток. Что если вам понадобятся унарные или тринарные операции?


                                                В функциональных языках есть один очень популярный паттерн — интерпретатор. Реализуется он обычно либо при помощи tagless final кодирования выражений, либо при помощи GADT. GADT в Rust нету, а вот простенький tagless final мы можем сделать используя трейты.


                                                Можно объявить трейт Expression


                                                trait Expression {
                                                  fn add(&self, right: &Self) -> Self;
                                                  fn sub(&self, right: &Self) -> Self;
                                                  fn negate(&self) -> Self;
                                                  fn eq(&self, right: &Self) -> Bool;
                                                }

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


                                                Для расширения Expression можно использовать "наследование" трейтов (хотя в большинстве случаев будет проще и лучше запихать операцию в изначальный трейт):


                                                trait ExpressionMul : Expression {
                                                  fn mul(&self, right: &Self) -> Self;
                                                }

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


                                                fn eval(i: i64) -> i64 { i }
                                                fn stringify(s: String) -> String { s }
                                                fn double<E1: ExpressionMul, E2: ExpressionMul>(pair: (E1, E2)) -> (E1, E2) { pair } 
                                                  +3
                                                  Дополню про tagless final хорошей статьей, которая объясняет, как именно этот подход решает упомянутую проблему выражения, примеры на хаскелле и джаве.
                                                  0
                                                  Использовать трейты и структуры — вполне себе идиоматично. А вот трейт-объектов старайтесь избегать: применяйте их только там, где действительно другие способы не работают.
                                                    –1
                                                    Прошу прощения за занудство, но вы решаете задачу неправильно и на ООП языке. Вычисление выражения с учётом приоритета операций и скобок не требует постройки деревьев, один довольно простой стековый автомат нужен чтоб преобразовать это выражение в бесскобочную обратную польскую запись например «a + b*c» -> «a b c * +», а "(a + b)*c" -> «a b + c*». Вычисление выражения, записанного в обратной польской записи реализуется на стековом автомате ещё проще. Можно объединить и получить автомат с 2мя стеками, который считает прямо. Стеки в расте есть.
                                                      0

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

                                                    0

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


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

                                                      +5
                                                      Это очень ведь дорого...

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


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

                                                        0
                                                        Согласен. Мне лично видятся очень хорошими варианты erlang+rust и golang+rust, и если выйдет история и webassembly, то js+rust.
                                                        +11
                                                        я научился писать простые сниппеты без ошибок с первого раза.
                                                        Это очень ведь дорого...

                                                        Зависит от того, с чем сравнивать:


                                                        • Можно взять php, начать писать на нём прямо здесь и сейчас, очень дешево вначале, дорого, когда в файле больше 200 строк и очень дорого в поддержке
                                                        • Можно взять js, на котором тоже можно быстро начать писать, но неявные приведения типов могут склонить вас сменить профессию с программирования на проституцию
                                                        • Можно взять C++, относительно дешево выучить его, писать проектики, а потом три месяца дебажить неопределенное поведение силами двух программистов с суммарным опытом в 20 лет.

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


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


                                                        • отсутствие большого количества нормальных production-ready пакетов, на данный момент на вкус и цвет 10000+ пакетов, годных и вылизанных от и до всего чуть больше 100-200 (а у С++ и того нет). Поправьте, если ошибаюсь.
                                                        • неоправданно большой граф зависимостей для некоторых пакетов (как если бы 90% npm зависили от left-pad).
                                                        • 200 пакетов компилируются за минуту, хотелось бы быстрее
                                                        • На данном этапе не понимаю, как интегрировать Rust futures в Js Promise для асинхронного WebSocket, чтобы перенести свой проект из нативного приложения в WASM. Решения на колбеках есть, но хотелось бы чего-то готового и поддержки в tokio/romio. Программистам на Go в этом плане сильно повезло, у них таких проблем нет. Они находятся на этапе: "почему мой hello world на WASM весит несколько мегабайт. Какой рантайм, какой ГЦ? Что это такое? Почему плохой транслятор GO-WASM тащит весь язык в файлик .wasm?"
                                                        • устал от HR, приглашают на работу и собеседования каждую неделю. Приходится вслушиваться в произношение людей из Шотландии и Новой Зеландии
                                                        • приходится скрывать доходы от друзей, потому что зарплату в $$$ реально девать некуда.
                                                          –1
                                                          А можно молотком забивать гвозди, а шилом делать дырки.

                                                          Я лишь про то, что rust прекрасный инструмент, когда мне нужен максимум скорости. Условно php7 может дать 30-40% от максимума; java — 60%; golang — 80%; и rust, c — 100%. Но в то же время чем ближе к C, тем каждую фичу ждать дольше и дольше, это с одной стороны. С другой, мы можем оценивать задачу по тому, сколько ей надо производительности и соответственно выбирать инструмент.

                                                          Про $$$ и golang вы тоже будете удивлены. Скажем так, 150-170k usd год на удаленной работе — не есть большая сложность.
                                                            +3
                                                            Я лишь про то, что rust прекрасный инструмент, когда мне нужен максимум скорости.

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


                                                            Когда мне надо наговнякать хоум страничку для друга, на которую никто не будет заходить — я выбираю PHP.
                                                            Когда мне надо в браузере работать с UI — я выбираю JS.
                                                            Когда мне надо набросать функциональщину для проверки гипотезы — я выбираю Haskell.
                                                            Когда мне нужен самый простой язык в мире, у которого 3 разных типа обработки исключений — нет, не выбираю :D


                                                            И когда мне нужен безопасный язык с предсказуемым поведением, который может поддерживать разработчик-июнь за 400USD, я выбираю Пикачу Rust, а не тот язык, поддержка которого стоит 150-170k USD в месяц (и то не предел).

                                                              0

                                                              Вопрос денег подняли вы.


                                                              Как план с junior должен сработать? Через какое время на rust он сможет полноценно закрывать задачи?


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

                                                                +1
                                                                Как план с junior должен сработать? Через какое время на rust он сможет полноценно закрывать задачи?

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


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

                                                                Ага. Чтобы корова меньше ела и давала больше молока ее нужно чаще доить и реже кормить.


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

                                                                  0

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

                                                                    +9
                                                                    А вы часто пишете на JS код (без IDE), который 100% работает во всех граничных случаях, не складывает случайно строки с числами и т.п. с первого раза? Или С++ код, на который ни один анализатор не ругнется ни одним правилом?
                                                              +4
                                                              Я лишь про то, что rust прекрасный инструмент, когда мне нужен максимум скорости.

                                                              Такое ощущение, что статью вы вообще не читали.
                                                                0

                                                                Читал. А вы мой изначальный комментарий?
                                                                "Хорошая статья.
                                                                Правда я придерживаюсь мнения, что язык, скорее системный, и мне проще и в пяток раз быстрее делать сервисы на го, а в раст выносить криптографию, системные вещи. Иначе цена решения становится несоразмерной."

                                                                  +5
                                                                  Читал. И, как можно догадаться, не согласен. От того, что вы решение некоторых проблем вынесете «на потом», в рантайм, лучше не становится. Вместо того, чтобы сделать фичу за 3 дня, делаем за день, а потом еще 2 дня дебажимся. Зато количество закрытых тасок удвоили. Вот замечательно-то.

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

                                                                    Как же все только живут с этими проблемами на рантайме… Тесты пишут, как и разработчики rust.


                                                                    Если говорить не голословно, то пока я видел одну неудачную миграцию на rust, которая сильно увеличила сроки проекта и поставила его под вопрос. Сейчас очень хочу узнать не об опыте крутых, без всякого сомнения, одиночек, а о больших и долгих проектах.
                                                                    FF с его 6% кода на rust не впечатлил пока, динамика там есть, но пока не видно, чтоб он занял существенную долю.


                                                                    И, повторюсь, вопрос долгой поддержки. Мне лично неясна ниша языка, пока мы не увидели проекты в долгой перспективе. У нас уже есть клевые scala, haskell, но которые слишком дороги в поддержке. Мне хотелось бы, чтобы rust не повторил эту судьбу.

                                                                      +1
                                                                      Как же все только живут с этими проблемами на рантайме…

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

                                                                      Тесты пишут, как и разработчики rust.

                                                                      В шарпе вы не пишете тесты на то, что вместо числа придет «ff», «qwerty» или ящереца в стакане, в JS пишете.
                                                                      В расте вы не пишете тест на то, что в многопоточном окружении ваш код не сломается, в тех же шарпах — пишите.

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

                                                                      В статье про PVS studio было прилично ссылок. Из того, что на слуху, можно вспомнить Parity/Exonum/Redox, например.

                                                                      FF с его 6% кода на rust не впечатлил пока, динамика там есть, но пока не видно, чтоб он занял существенную долю.

                                                                      Когда я последний раз смотрел статистику по репозиторию, в FF было 1млн строк кода на С, 1.5 миллиона на расте, 3.5 (или 7, не помню точно) миллиона на С++, и около 5 миллионов всякой шелухи вроде html.

                                                                      И, повторюсь, вопрос долгой поддержки. Мне лично неясна ниша языка, пока мы не увидели проекты в долгой перспективе. У нас уже есть клевые scala, haskell, но которые слишком дороги в поддержке. Мне хотелось бы, чтобы rust не повторил эту судьбу.

                                                                      Буквально в прошлом месяце у меня знакомый сменил синиор шарпа позицию на синиор скалиста. Не так уж у неё все плохо.

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

                                                                        Статистику тут обычно смотрю https://4e6.github.io/firefox-lang-stats/


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

                                                                          0
                                                                          Огромное спасибо, давно искал эту ссылку! Сильно наврал с цифрами, прошу прощения. Помню примерно «1.7 раста на 7 С++», остальное хуже.

                                                                          Да, то верная информация.

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

                                                                          Откуда взятся проектам сильно за год, если язык только 3 года назад в 1.0 вышел? Ведь там менеджеры тоже по той же логие смотрят «пока только появилось, надо обождать, присмотреться, и только потом осваивать».

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

                                                                          Более крупного пока ничего нет, но опыт самого rustc показывает, что и довольно крупные проекты вполне неплохо живут.
                                                                            +1

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


                                                                            Мне лично пока видится, что врядли и нет. Посмотрим.

                                                                              +4

                                                                              Я извиняюсь если вопрос покажется некорректным. Но мне правда интересно. Вы бы стали писать на Golang если бы за ним не стоял Гугл? И что произойдёт если гипотетически Гугл скажет "голанг неудачен, пилим всё на тайпскрипт"?
                                                                              Озвученная вами проблема — извечная проблема курицы и яйца. Никто не хочет писать на новом языке т.к. на нём не пишут толстые корпорации — которые на нём не пишут т.к. пишет мало кто, goto 1. Почти гарантирую, что если бы С++ был создан сейчас в текущем виде, он бы помер не родившись — но его держат мегатонны легаси.

                                                                                +1
                                                                                Хороший вопрос. У меня в активе языков есть такие вещи как R, closureScript, так что вряд ли я гонюсь за популярными вещами и большими компаниями.

                                                                                Да, я выбирал язык не из-за Гугла. У меня был PHP, Python, плюс всякое редкое (по месяцу пробовал Nim, Crystal), но не было чего-то достаточно быстрого, клево себя в concurrency и строго типизированного. Рассматривал варианты C#, с которым был год опыта, когда он еще был версий 1.1-1.3, Java, С++, golang.
                                                                                C# отмелся поскольку совсем другой стек все же. Хотя как язык он мне очень нравился.
                                                                                Java — слишком большая штука. Ее надо брать не дополнительным инструментом, а единственным и для всего. Но окончательно я ее не отметал.
                                                                                С++ — я еще помню долгие споры об Oberon/modula/Pascal vs C/C++ и брать язык с всевозрастающей собственной сложностью — это точно нет.
                                                                                Golang обещал полную обратную совместимость (слово сдержано и с 1.0 по 1.13 ломающих изменений было ровно 2, которые фиксились автоматически гошной же тулзой), приятное мне смещение внимания с языка на продукт (когда можно не изучать и изучать язык каждый год, а заниматься развитием продуктов), почти полная имплементация CSP (за исключением операции удаления потока), очень (очень-очень) быстрая компиляция, что делала работу в TDD удобной и комфортной. Это перевесило, я начал его учить. Скоро и вакансия нашлась.

                                                                                Нет, про гугл я тогда не думал. В основном думал про те задачи, которые хотелось решать. А это был e-commerse и highload. Там golang себя хорошо нашел. Сейчас занимаюсь распределенными системами и golang себя все еще хорошо чувствует. Как бонус получил golang на мобильных устройствах и есть опыт уже разработки гошных либ для мобильных приложений.

                                                                                Но не хватает иногда быстрой числодробилки. Можно пойти путем C и его биндингов в Golang. Но rust, хоть также идет по пути усложнения и увеличения объема языка, как С++, но обещает хорошую модель конкуренции и быструю числодробилку, поэтому решил его тоже взять.

                                                                                Как-то так. Не думаю, что ваше дальнейшее рассуждение о курице и яйце ко мне применимо. У меня другие критерии.
                                                                    +3
                                                                    Проще для кого? Для человека с 2 годрами опыта на Rust и без опыт Go Проще в пяток раз будет на Rust сделать весь миросервис и работать он будет быстрее и надежнее. Да в целом код на Rust писать мне приятнее и проще чем на Go хотя и там и там у меня опыт одинаковой величины. Просто потому что в Rust есть шикарные enum и генерики поэтому не приходиться костыли делать как в Go с interface{} или вообще, прости господи, кодогенерацией.
                                                                      0

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

                                                                        +1
                                                                        мне проще и в пяток раз быстрее делать сервисы на го

                                                                        Это вы начали давать субъективные оценки. Я вам лишь ответил на вашем же языке.
                                                                          –1

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

                                                                            +1
                                                                            Вы путаете скорость и простоту разработки со скоростью и простотой изучения. Да и вообще, вы как это мерили, посадили в разных комнатах Go и Rust программиста с опытом 1+ год и дали им задачу написать одинаковый микросервис и писать до тех пор пока он не проходит корректно все тесты или это просто ваши голословные утверждения по поводу того что на Go разрабатывать проще и быстрее или на самом деле может на Rust проще и быстрее?
                                                                              +1

                                                                              У меня есть в команде разработчик: 2 года go, затем год rust, и вот снова go.
                                                                              На нем удобно сравнивать. На нем и сравниваем.
                                                                              Задачи на го идут шустрее в разы.


                                                                              Ну и я тоже изучаю раст сейчас, пока то, что я вижу точно говорит:


                                                                              1. Код ревью будут сложными и могут быть долгими
                                                                              2. Вход разработчика в проект тоже долгий
                                                                              3. Начала работы новичка в языке — несколько месяцев и точно нужно приставлять наставника.

                                                                              Изучаю дальше.

                                                                                +4
                                                                                Вам половину ревью сделают компилятор, rustfmt и clippy. Писать на Rust новый код чуть долше, но вот рефакторить ранее написанный — довольно быстро и приятно. А главное — появляется уверенность в его надежной работе с технической стороны, можно больше внимания уделять проверке логики (даже по сравнению с Java, где у меня на большом проекте примерно 80% времни уходило на исправления NPE-багов).
                                                                                  0
                                                                                  Все равно надо код ревьюить и пока мне видится, что при большом объеме фич в языке и довольно «особенном» синтаксисе, подчас, приведет к трудным ревью. Это вопрос, с какой скоростью можно понимать чужой код на rust.
                                                                                  Для меня вопрос открытый, хоть и есть свое мнение.
                                                                                    0
                                                                                    Фич не так уж много, как может показаться. Растбук сравнительно небольшой (реально прочитать за день, если с растом хоть немного знаком), и покрывает всё, что есть в языке.

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

                                                                                    Это вопрос, с какой скоростью можно понимать чужой код на rust.

                                                                                    Раст это не «новый С++», там нет непересекающихся подмножеств языка, где каждый разработчик пишет на «своём» диалекте и не понимает соседей.

                                                                                    Новая версия раста выходир за в 6 недель, и как правило выходит какая-нибудь мелкая фича, как правило, снятие существовавшего раннее ограничения, стабилизация пары полезностей в стандартой библиотеке и прочая мелочь. Раз в год примерно выходят крупные фичи, вроде того же NLL, или готовящегося async/await и генераторов. Не сказал бы, что это прям очень часто, какой-нибудь C# обновляется примерно так же раз в год.
                                                                                      0
                                                                                      Раст это не «новый С++», там нет непересекающихся подмножеств языка, где каждый разработчик пишет на «своём» диалекте и не понимает соседей.

                                                                                      Мне это пока сомнительно. Пока выдится именно так. И повторюсь, в сравнении с golang, который поддерживает полную обратную совместимость и даже golang 2.0 взял на себя гарантию, что код любой версии 1.0 будет компилироваться без изменений, изменения в rust смотрятся дополнительной ценой, которую надо платить.

                                                                                      Возможно через полгодика изучения языка количество фич и особенностей не будет казаться таким большим, но пока оно именно такое и пока думается, что это вряд ли изменится, все же язык явно наследник именно c++.
                                                                                        0
                                                                                        Мне это пока сомнительно. Пока выдится именно так. И повторюсь, в сравнении с golang, который поддерживает полную обратную совместимость и даже golang 2.0 взял на себя гарантию, что код любой версии 1.0 будет компилироваться без изменений, изменения в rust смотрятся дополнительной ценой, которую надо платить.

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

                                                                                        А вот будет ли совместимым Go 2.0 — не уверен. Судя по генерикам — вряд ли.

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

                                                                                        Мое мнение, что аналогия ложная. Впрочем, вам решать.
                                                                                          0
                                                                                          Поясните про генерики и 2.0. Старый гошный код не должен от них ломаться, судя по драфту.
                                                                                            0

                                                                                            Ну хорошо, если так.


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


                                                                                            Если команда го всё это смогла учесть, то могу их только поздравить.

                                                                                              0

                                                                                              Бинго! Нет перегрузки.


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

                                                                                  +1

                                                                                  А я знаю пример, когда разработчик "въехал" и начал нормально писать на расте за пару недель. До этого он писал на го и скале.

                                                                                    –1
                                                                                    И свободно говорит на 15-языках?

                                                                                    Речь про начинающих.
                                                                                    –1
                                                                                    Я бы рекомендовал посмотреть VanquisherWinbringer публикации и код из них, прежде чем обсуждать с ним и делать далеко идущие выводы =)

                                                                                    На мой взгляд, код Раста трудночитаем -> мало кто будет на нем писать со всеми вытекающими. Так что согласен.
                                                                                      0
                                                                                      Я б рад, но времени не так много, чтобы дополнительные статьи читать.
                                                                                      Тут, скорее, было интересно понять, как складывается отечественное сообщество вокруг языка. Оно определенно складывается. И, к сожалению, должен признать, что оно пока более дружелюбно, чем в свое время гошное. Хотя и тут не без криков о вкусах и понятий вроде «ненавижу язык Х»!

                                                                                      А вы, товарищ, не пробовали rust? Было бы интересно обсудить.
                                                                                        0
                                                                                        Думаю, не буду участвовать в истерии. Кроме синтаксиса и практического неудобства, раст еще и сырой.

                                                                                        Захочется приключений — вернусь в dlang — он на 5 лет старше и хотя бы прошел детские болезни и оброс фремворками. Ну или уж посмотрю на golang — он практически стабилизировался (еще бы ввели ожидаемую обработку ошибок).
                                                                                          0

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

                                                                                            +1
                                                                                            Захочется приключений — вернусь в dlang — он на 5 лет старше и хотя бы прошел детские болезни и оброс фремворками.
                                                                                            Где-то можно посмотреть список живых фреймворков для D? На слуху как-то кроме Vibe.d ничего и нет.
                                                                                              0
                                                                                              code.dlang.org причем с версионированием пакетов и родной системой сборки.

                                                                                              Но D я тоже хвалить не буду — у него тоже есть свои критические (на мой взгляд) недостатки.
                                                                                                0
                                                                                                Жаль, ответа по сути не будет. Куча пакетов с номерами версий вроде 0.0.6 или 0.2.0 — на «оброс фремворками» не тянет. Даже если вести отсчет от появления D2.
                                                                                                  0
                                                                                                  Ну «маемо, шо маемо». Это ж, опенсорс =)

                                                                                                  Vibed тоже имеет версию 0.8, но проходит все тесты…
                                                                                                    +1
                                                                                                    Ну просто если сравнить с тем же crates.io, то можно сказать, что в D ничего-то и нет. 1.5K пакетов для D против 21K оных для Rust-а.
                                                                                          +3
                                                                                          На мой взгляд, код Раста трудночитаем -> мало кто будет на нем писать со всеми вытекающими. Так что согласен.

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

                                                                                            0
                                                                                            Фу таким быть — обычно к подходу, у меня больше опыта поэтому я знаю как лучше прибегают глуповатые люди не способные аргументировать свое мнение. Поэтому я бы к вашим комментариям тоже отнесся скептически. Ну раз уж на то пошло то я лично участвовал в разработке информационной системы международного уровня, системы федерального значения и одной системы регионального уровня. Да это все было на C#. + Разрабатывал и проектировал с нуля клиент — серверное приложение для одной Туристической компании которым они года два пользовались. Сейчас как там дела уже не знаю. Я же не буду сюда выкладывать код с тех проектов. Да и на гитхабе всякую фигню делаю как попало для души. И да, я больше десятка разных языков из любопытства пробовал и пока что мне из них Rust нравиться.
                                                                                              –2
                                                                                              Вообще то в моем комментарии нет абсолютно никакого негатива. Каждый пусть делает выводы сам.

                                                                                              Только легкое подкалывание Эксперта по языку со статьей от 5 ноября 18г, «Изучаю Rust....» =)
                                                                                              +4
                                                                                              Rust трудночитаем только для тех, кто на нем не программирует. Потому что синтаксис непривычный. Но он довольно простой и проблем с восприятием чужого кода, если он не перегружен лайфтаймами (что бывает редко), обычно не возникает. Читать исходники зависимостей проекта — обычная практика в Rust. Часто это быстрее и проще, чем смотреть документацию.
                                                                                                +1
                                                                                                Сейчас отсюда последует leap of logic, что с документаций в расте все плохо :)
                                                                                                  +1

                                                                                                  И ведь подумал, что надо дописать: "хотя с документацией в Rust все в порядке", но не стал. А зря :)

                                                                                                    –4
                                                                                                    Логика другая — вот я могу читать десяток-два языков и беглый взгляд обычно дает понятие — что тут происходит.

                                                                                                    Без документации — она потребуется позже, чтобы нормально писать.

                                                                                                    Rust source — WTF ???
                                                                                                      +5
                                                                                                      Языки семейства ML и Haskell, например, входят в ваш десяток? Может быть те, которые вы можете читать бегло — это всё вариации примерно одного и того же языка (или двух)?
                                                                                                        +2

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

                                                                                +2
                                                                                Можно взять C++, относительно дешево выучить его, писать проектики, а потом три месяца дебажить неопределенное поведение силами двух программистов с суммарным опытом в 20 лет.
                                                                                А как именно Rust защищает от ошибок с низкоуровневой работой с неправильно выровненными данными (ведь ваша ссылку ведет на исправление именно такой проблемы)?
                                                                                  +5

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

                                                                                    +6

                                                                                    Rust не защищает тебя на 100% одним своим присутствием в проекте, но позволяет вывести строгий аргумент безопасности, которые сведут количество подобных ошибок к минимуму, если не к нулю: не пиши unsafe. Если они и возникают, то ты всегда ищешь там «где светло», а светло там, где есть unsafe.


                                                                                    А как именно Rust защищает

                                                                                    У меня вчера был сложный вечер. Меня порывало сказать: "Да никак! Это настолько сложная ошибка, что даже Rust пасует перед ней!", потому что я был под влиянием неприятных воспоминаний, когда не было никакой возможности найти зацепку в коде C++, а только лишь дебажить и дебажить. Как получить зацепку для дебага кода C++, если он весь unsafe со списком в ~200 неопределенных поведений? Ну… делать вот так o_O и искать, искать, искать. Ошибки подстерегали нас на каждом углу, даже сложению знаковых чисел нельзя было доверять. Хотя казалось бы, самая примитивная операция.


                                                                                    На этом можно было бы и закончить комментарий, мол, Rust не защищает, но тут в дело вступает маленький нюанс: а где бы я мог выстрелить в ногу, если бы я писал виртуальную машину на Rust? Только в unsafe. И путем нехитрых изысканий приходим к тому, что unsafe мне бы нужен был только для низкоуровневой работы с памятью. Всё. Делаем o_O на 500 строчках кода, обмазываемся тестами, перепроверяем только код сборщика мусора. Rust выигрывает не в своем фанатичном "безопасно", а в предоставлении системного подхода к поиску низкоуровневых проблем. Ищи там, «где светло».


                                                                                    Что делать, когда этого «светло» нет? Казалось бы, есть ноды https://nodes.tox.chat/, написанные на C, которые уже 5 лет в проде, оттебажены, в которых все хорошо, которые не падают… Ведь так?


                                                                                    The Red Downtime


                                                                                    Вот ты разработчик, у тебя падает код в 60KLOC, который 5 лет "правильно работал". Что ты будешь делать? Я бы заплакал.


                                                                                    Ну и в противовес: tox-rs (30KLOC) без единого unsafe. И наш сервер не падает в рандомных местах. Не утекает, не ломается. И не утечет, не сломается.


                                                                                    Тут стоит напомнить о свежей баге, найденной в tar, которому 40 лет:


                                                                                    https://utcc.utoronto.ca/~cks/space/blog/sysadmin/TarFindingTruncateBug


                                                                                    if you run GNU Tar with --sparse and a file shrinks while tar is reading it, tar fails to properly handle the resulting earlier than expected end of file. If the file grows again, tar recovers.

                                                                                    Зато на C. И очень быстра.

                                                                                      –9
                                                                                      Грубо говоря, вы слились.

                                                                                      Ибо была ссылка на конкретную ошибку в плюсовом коде с явным намеком на то, что в Rust-е вы бы от такой были бы защищены. Но проблема в том, что там была ошибка с низкоуровневым кодом, для понимания которой нужно было опускаться на уровень аппаратной архитектуры и особенностей команд конкретного процессора. Rust бы вам магическим образом ничем бы не помог.
                                                                                      Я бы заплакал.
                                                                                      Тряпка! :)
                                                                                      И наш сервер не падает в рандомных местах. Не утекает, не ломается.
                                                                                      Вас может удивить, но на плюсах совсем не сложно сделать сервер, который не падает, не утекает, не ломается. Мы делали это неоднократно.

                                                                                      Но речь про плюсы, не про С. А вы, как и многие здесь, упорно проводите между ними знак равенства.
                                                                                        +2
                                                                                        Грубо говоря, вы слились.

                                                                                        Действительно грубо.

                                                                                        А можете привести пример проекта вот с этими свойствами (желательно со ссылками, подтверждающими эти свойства):
                                                                                        на плюсах совсем не сложно сделать

                                                                                        сервер, который не падает, не утекает, не ломается

                                                                                        Мы делали это


                                                                                        Интересно узнать о проектах какой сложности идет речь.
                                                                                          –2
                                                                                          Действительно грубо.
                                                                                          Как народец-то обмельчал. Слово «слились» — это уже грубо. Суппорт старого кода всего в 60KLOC — «я б заплакал».
                                                                                          А можете привести
                                                                                          Из публично доступного у нас есть вот это. Игрушка, с собственной реализацией HTTP-сервера.
                                                                                          Интересно
                                                                                          Мне вот было интересно узнать, как Rust защищает от ошибок выравнивания данных в низкоуровневом коде. Но что-то не вышло.

                                                                                          Может вы поддержите предыдущего оратора?
                                                                                            +2
                                                                                            Мне вот было интересно узнать, как Rust защищает от ошибок выравнивания данных в низкоуровневом коде. Но что-то не вышло.

                                                                                            Если только синтаксически значительно сужает зону, где они могут появиться, чем облегчает и кодирование, и поиск ошибки. Но не радикально.
                                                                                            В C/C++ то же самое достигается с помощью правильной архитектуры, строгого следования code guidlines и статического анализа. Т.е. более трудоемко. Это цена большей выразительности (на низком уровне — точно) и излишней мягкости Б. Страуструпа.
                                                                                              –1
                                                                                              О том и речь.

                                                                                              Причем я бы не стал смешивать C и С++ здесь. Т.к. в С++ столкнувшись с такой ошибкой можно было бы сделать шаблонный тип вроде properly_aligned_ptr<T> и изменить API так, чтобы интерфейс оперировал такими типами, а не голыми указателями. Тогда как в C пришлось бы следовать устным договоренностям.
                                                                                                +2
                                                                                                Тогда как в C пришлось бы следовать устным договоренностям.

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


                                                                                                Понятно.

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

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

                                                                                                    Я в курсе, что это разные языки. Я так же в курсе, что на C++ можно писать как на C (за исключением некоторых очень специфичных штук типа structure initializer). А раз это можно делать, то этим люди и занимаются because they can.


                                                                                                    И вашем мире розовых пони существует супер-классный C++17-20 (он мне тоже нравится), но на котором можно безопасно писать только с соблюдением устных договоренностей ("Срочно пишем по CppCoreGuidelines"), а в моем мире существует махровый C++, дай бог C++11, который пахнет как гавно, выглядит как говно, на вкус как говно. Его практически невозможно отличить от C, но типа в .cpp файликах, ага.

                                                                                                      +1
                                                                                                      Мыши плакали, затыкали нос, но..?
                                                                                                        +5

                                                                                                        Но поддерживали легаси за зп.

                                                                                                        +1
                                                                                                        Простите, где подтверждения хотя бы каких-либо ваших утверждений?

                                                                                                        По поводу современного C++ и CppCoreGuidelines, то могу вам сказать, что примитивные шаблоны вроде not_null или bounded_value, как и смарт-поинтеры и прочие вещи, облегчающие RAII, нормальные разработчики использовать стали задолго до. Где-то даже до принятия C++98 (шаблоны, если вы не в курсе, более-менее массово доступны стали с 1994-1995-х годов). Книги вроде Modern C++ Design и C++ Coding Standards — 101 Rules Guidelines — это 2001-й и 2004-й годы. По мерками ИТ совсем давно.

                                                                                                        Говнокод можно наплодить везде. Счастье Rust-а в том, что до него еще говнокодеры из C++, Java и JavaScript-а не добрались. Как доберутся в массовых количествах, так запаритесь unsafe расчищать.
                                                                                                          +3

                                                                                                          Счастье Rust'а в том, что на нем написать нормальный код проще, нежели наговнокодить.


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

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

                                                                                                            Но я тоже жду, пока «оптимизаторы» из C++ перебегут к вам.
                                                                                                              0

                                                                                                              Про двусвязные списки уже давно не актуально.

                                                                                                                0
                                                                                                                Автору статьи про это расскажите. Он почему-то счел заслуживающим внимание этот вопрос затронуть. Хотя казалось бы.
                                                                                                                  –2
                                                                                                                  Про двусвязные списки уже давно не актуально.

                                                                                                                  в листинге на 1k строк 26 unsafe, в которых спрятана примерно половина кода.
                                                                                                                    +2

                                                                                                                    Да-да, мы это уже проходили. Ищите другой аргумент, чем плох Rust.

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

                                                                                                          Rust бы вам магическим образом ничем бы не помог.

                                                                                                          Я объяснил, как именно он бы мне магически помог.

                                                                                                            0
                                                                                                            Я объяснил, как именно он бы мне магически помог.
                                                                                                            Ошибку Rust бы вам предотвратил? Нет.

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

                                                                                                            У меня лет 20 назад был похожий случай. В коде по десериализации двоичных данных было что-то вроде:
                                                                                                            float parser::get_float() {
                                                                                                              const char * ptr = m_current_ptr;
                                                                                                              m_current_ptr += 4;
                                                                                                              return *((const float *)ptr);
                                                                                                            }

                                                                                                            На x86 работало нормально. На SPARK-е сразу же SIGBUS. И нашлось без проблем.
                                                                                                              +1
                                                                                                              Все остальное — это спекуляции на тему того, как быстро конкретный человек в конкретном коде бы нашел проблему.

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


                                                                                                              И поэтому я полез в код GC на Rust: withoutboats/shifgrethor, (начало цикла статей).


                                                                                                              Вы знаете, код проще читать, тесты проще писать, баги проще искать. Ночью набил пару PR.

                                                                                                                –4
                                                                                                                код проще читать, тесты проще писать,
                                                                                                                Кому-то код на Lisp-е проще читать, чем код на Pascal.
                                                                                                                баги проще искать.

                                                                                                                Ну надо же, баги. Казалось бы, откуда им взяться, раз и Rust такой весь из себя, и говнокодеры из C++ еще не подтянулись. Неувязочка ;)
                                                                                                        +1
                                                                                                        C, все-таки, существенно проще и в него лучше «входят» расширения для поддержки аппаратных/программных «выкрутасов» (типа TR 18037). ИМХО, в определенной сфере применения он удобнее C++, в котором герберизм-саттеризм не дает заниматься делом))).
                                                                                                          0
                                                                                                          Возможно. Разработчики аппаратуры наверняка входят в C проще.
                                                                                                          Но это уже совсем офтопик.
                                                                                                            0
                                                                                                            Ну, думаю, что тут главное то, что C для работы на «низком» уровне выразительности хватает, а вот абстракций и концепций, требующих нетривиального рантайма нет. «C++ как улучшенный C» это, ИМХО, преувеличение. Помнится, в первом издании «Языка программирования C++» в финальном примере Страутсруп реализовал драйвер вывода на экран на C, чтобы подчеркнуть, как он писал, «разделение зоны ответсвенности» между этими языками. Как-то так, мне нравиться писать и на C, и на C++.
                                                                                                0
                                                                                                Вот ты разработчик, у тебя падает код в 60KLOC, который 5 лет «правильно работал». Что ты будешь делать? Я бы заплакал.

                                                                                                boost::stacktrace, google breakpad, в с++20 едет std::stacktrace. Ошибка локализуется еще на этапе чтения логов, до открытия IDE. Да и в целом краши — самые легко отлаживаемые баги. А если вдруг у вас сервер вернул «3» вместо «14», где «светло»?

                                                                                                И наш сервер не падает в рандомных местах. Не утекает, не ломается.

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

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

                                                                                                  Согласен. Безопасные пишутся. В 0.1%. А в 99.9% на плюсах пишутся небезопасные программы. И это не зависит ни от моего, ни от вашего, ни от чьего-бы-то-ни-было еще мнения по поводу плюсов/раста.


                                                                                                  boost::stacktrace, google breakpad, в с++20 едет std::stacktrace.

                                                                                                  А как это бы помогло отдебажить ошибку внутри C++ функции, которую вызывал JIT код, собранный самодельной VM, которая проявлялась только на g++ -O3 -march=native с кучей проходов LLVM?

                                                                                                    0
                                                                                                    внутри C++ функции, которую вызывал JIT код, собранный самодельной VM, которая проявлялась только на g++ -O3 -march=native с кучей проходов LLVM?
                                                                                                    Так что есть в Rust для предотвращения этой ошибки? Вы поместите такой же for внутрь unsafe блока и получите те же проблемы.
                                                                                                      0
                                                                                                      Безопасные пишутся. В 0.1%. А в 99.9% на плюсах пишутся небезопасные программы

                                                                                                      статистику в студию.

                                                                                                      А как это бы помогло отдебажить ошибку внутри C++ функции, которую вызывал JIT код, собранный самодельной VM, которая проявлялась только на g++ -O3 -march=native с кучей проходов LLVM?

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

                                                                                                        Он был правильно сгенерирован. Я спрашиваю каким образом можно отследить фреймы и распечатать stacktrace, если его нет или он покорёжен неопределенным поведением?

                                                                                                          0
                                                                                                          каким образом можно отследить фреймы и распечатать stacktrace, если его нет

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

                                                                                                          или он покорёжен неопределенным поведением

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

                                                                                                            При чём тут она? В слове «stacktrace» «stack» присутствует не просто так, и он на запись не лочится никогда.


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


                                                                                                            Теперь вопрос — что будет, если эти адреса повреждены? Даже если в стеке есть маркеры (которых обычно нет — я слышал, что они добавляются какой‐то программой, используемой для отладки, но сейчас не вспомню, какой), то это будет спекуляцией — ничто не запрещает маркеру присутствовать в переменных, размещённых на стеке. А если их там нет, то в стеке почти наверняка будут и указатели на стек, и указатели на код, не имеющие никакого отношения к адресам начала кадра и возврата.

                                                                                                        +4
                                                                                                        статистику в студию.

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


                                                                                                        Так что нет здоровых безопасных, есть недообследованные неоптимизированные.

                                                                                                          0
                                                                                                          а с каким количеством программ полностью на с++11 и выше вы имели дело?
                                                                                                          upd. имеются в виду программы, написанные в соответствии с практиками современного с++, а не с++03 код с другим флагом компилятора
                                                                                                            0
                                                                                                            Да давайте сделаем проще. Существование доказать сильно проще, чем универсальность. Покажите достаточно большую программу на С++, в которой нет UB.

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

                                                                                                              но вы сами сказали, что существование доказать сильно проще. 0xd34df00d утверждал что все с++ проекты, с которыми он имел дело, имели UB. Я попросил лишь назвать число таковых, на современном с++. Вы сами прекрасно понимаете, что выкладывать примеры коммерческого софта (с которым я имел дело) я не могу.

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

                                                                                                              Вы говорите о локальном использовании implementation-defined поведения, но забываете, что раст не стандартизован вообще и в нем всего один компилятор. В терминологии с++, поведение раста на 100% «implementation-defined».
                                                                                                                +4

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


                                                                                                                Вы говорите о локальном использовании implementation-defined поведения

                                                                                                                Речь идёт о нестандартных расширениях GCC -fwrapv, -fno-strict-aliasing и др. Согласно стандарту соответствующие конструкции вызывают undefined behavior. Так что ядро линукса — нестандартный C и до недавнего времени могло компилироваться только с помощью GCC.


                                                                                                                В терминологии с++, поведение раста на 100% «implementation-defined».

                                                                                                                Есть The Rust Reference и там не написано, что всё поведение — implementation defined. Отсутствие стандарта не означает, что ничего не определено. Кстати, C был стандартизирован через 17 лет после появления — 29 лет назад, а ядро линукса написано на нестандартном C. В общем, не стоит преувеличивать ценность стандартизации.

                                                                                                                  –2
                                                                                                                  Но позиция людей, говорящих, что им так нравится и всё в порядке, вызывает некоторое удивление

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

                                                                                                                  Согласно стандарту соответствующие конструкции вызывают undefined behavior. Так что ядро линукса — нестандартный C и до недавнего времени могло компилироваться только с помощью GCC.

                                                                                                                  «undefined behavior» значит «поведение, не определенное стандартом», а не «ошибочное поведение». Компилятор имеет право корректно обрабатывать UB не нарушая при этом стандарт. Программа при этом, разумеется, становится не стандартной, а привязанной к конкретному компилятору. Тот же статус у всех rust программ

                                                                                                                  Есть The Rust Reference и там не написано, что всё поведение — implementation defined

                                                                                                                  но rust reference не первичен, это просто описание текущего поведения компилятора. При изменении компилятора меняется rust reference, а не наоборот.
                                                                                                                    0
                                                                                                                    > Компилятор имеет право корректно обрабатывать UB не нарушая при этом стандарт

                                                                                                                    Зачем тогда эти ключи в GCC? Да и применимость определения «корректная обработка» в случае UB вызывает сомнения. Корректная согласно здравому смыслу? У комитета по стандартизации не достаёт здравого смысла? Или что-то другое?

                                                                                                                    > При изменении компилятора меняется rust reference, а не наоборот.

                                                                                                                    Почему? Может удобнее? Как с C++ делают: обкатывают экспериментальные реализации фич в компиляторах, а потом вносят в стандарт. А то иногда нехорошо получается, как с extern template. Кстати, дополнительная реализация Rust уже есть: mrustc.
                                                                                                                      0
                                                                                                                      Да и применимость определения «корректная обработка» в случае UB вызывает сомнения.

                                                                                                                      Корректность ПО — его способность реализовывать требуемый алгоритм в определенных условиях. Ни больше, ни меньше. Полагаться на документированное поведение компилятора — ваше право (так же, как и в расте). Стандарт лишь описывает требования к компиляторам.

                                                                                                                      Как с C++ делают: обкатывают экспериментальные реализации фич в компиляторах, а потом вносят в стандарт

                                                                                                                      при этом чаще всего в стандарт попадает не версия из компилятора, а измененная/исправленная комитетом.
                                                                                                                      Разница огромна, и она в следующем: если вдруг какой-то метод в расте будет вести себя вопреки конкретно вашим ожиданиям, нет ни одного документа, с помощью которого можно было бы ответить на вопрос «это баг или фича (или implementation defined)?».

                                                                                                                      Кстати, дополнительная реализация Rust уже есть: mrustc.

                                                                                                                      и нет ни единого расхождения в поведении этих двух компиляторов? А если они есть, то в каком из них ошибка?
                                                                                                                        +1
                                                                                                                        > А если они есть, то в каком из них ошибка?

                                                                                                                        Думаю обсудят и решат, где ошибка. Если, что-то описанное в reference вызывает unsoundness в системе типов, поправят reference. Если программа скомпилированная mrustc/rustc не реализует требуемый алгоритм в условиях, описанных в reference, поправят компилятор. Если расходится поведение не описанное в reference, доопределят reference и поправят соответствующий компилятор или поправят и доопределят. Если mrustc не компилирует программу из-за того, что в rustc добавили новую фичу (о чём можно узнать у разработчиков языка, или, если хочется иметь текст, попросить их внести это в reference) — очевидно, что нужно добавить новую фичу в mrustc и reference.

                                                                                                                        Я понимаю, что тяжело привыкнуть к ситуации когда неформальный комитет по стандартизации и разработчики языка — одна и та же группа, но 17 лет ещё не прошло, так что Rust потенциально может обогнать C по срокам формальной стандартизации.
                                                                                                                          –1
                                                                                                                          Если ...

                                                                                                                          По сути «если у 2-х из 3-х одинаково, то последний неправ». А если везде (reference/rustc/mrustc) по-разному? А если всё-таки неправы двое?

                                                                                                                          так что Rust потенциально может обогнать C по срокам формальной стандартизации.

                                                                                                                          вы спорите со мной сегодня. На сегодняшний день у раста стандарта нет.

                                                                                                                          Но речь не совсем об этом. А о том, что весь rust код на данный момент implementation-defined. И в этом, на мой взгляд, нет ничего ужасного. Я лишь указываю на иронию ситуации, в которой фанаты раста критикуют c++ за использования поведения, не определенного стандартом
                                                                                                                            0
                                                                                                                            По сути «если у 2-х из 3-х одинаково, то последний неправ». А если везде (reference/rustc/mrustc) по-разному? А если всё-таки неправы двое?

                                                                                                                            Сделают то же самое, что делают во всех языках при обнаружении дыр в стандарте — доопределят стандарт (в данном случае неформальный). Вам обязательно печать IEEE/ISO/ANSI нужна?


                                                                                                                            Я лишь указываю на иронию ситуации, в которой фанаты раста критикуют c++ за использования поведения, не определенного стандартом

                                                                                                                            Да нет никакого тотального implementation defined в смысле C++. Есть неформальный стандарт, частью описанный в reference, частью описанный кодом rustc. В reference явно указывается, что implementation defined, а что — нет, и что — undefined behavior.


                                                                                                                            Кроме того, никто С++ за implementation defined не ругает. Ругают за кучу возможностей получить undefined behavior в любом месте программы без явного обозначения этих мест.

                                                                                                                              0
                                                                                                                              Документация раста генерируется напрямую из комментариев в коде. Поэтому нет никакого «неформального стандарта», есть лишь описание текущей реализации.

                                                                                                                              Кроме того, никто С++ за implementation defined не ругает.

                                                                                                                              хм

                                                                                                                              Ругают за кучу возможностей получить undefined behavior в любом месте программы без явного обозначения этих мест.

                                                                                                                              чтобы явно пометить места, где можно/нельзя получить неопределенное поведение, надо сначала определить поведение. Чуете иронию?
                                                                                                                                +1
                                                                                                                                Документация раста генерируется

                                                                                                                                Я говорил про The Rust Reference. Она из кода не генерируется.


                                                                                                                                хм

                                                                                                                                Нестандартное расширение компилятора и implementation defined — разные вещи. Если бы в стандарте было написано, что знаковое переполнение — implementation defined, то не понадобился бы ключ -fwrapw, который переводит компилятор в нестандартный режим, в котором знаковое переполнение определено как two's complement.


                                                                                                                                надо сначала определить поведение. Чуете иронию?

                                                                                                                                Нет, не чую. Стандарты не снисходят с небес, а проходят процесс развития. Rust сейчас в стадии, когда рано его формально стандартизировать. Но это не значит что программа на Rust'е может делать всё что угодно, так как в The Rust Reference не написано, что, скажем, глубина анализа при выведении типов — implementation defined.


                                                                                                                                Формальная стандартизация — не магический ритуал, делающий язык лучше. Это индикатор того, что язык достиг определённой степени зрелости. Поэтому я и упоминаю постоянно 17 лет нестандартного С, чтобы напомнить что ничего особенного тут нет.

                                                                                                                                  –1
                                                                                                                                  implementation-defined отличается от undefined behavior только тем, что первое компилятор обязан как-нибудь реализовать, а второе — нет. Однако это не значит, что компилятор обязан оставить поведение неопределенным. Например, «положить в union значение одного типа а достать — другого» это UB по букве стандарта. Однако, такое поведение определено во всех основных компиляторах одинаково и повсеместно используется.

                                                                                                                                  Еще надо отметить, что «нестандартные расширения» — следствия стандартизации. В расте таковых не наблюдается только из-за отсутствия стандарта, а не потому что он такой классный

                                                                                                                                  так как в The Rust Reference не написано, что, скажем, глубина анализа при выведении типов — implementation defined.

                                                                                                                                  rust reference вообще никакое поведение не определяет, а лишь описывает. И вообще ни один документ поведение компилятора раста не определяет

                                                                                                                                  Формальная стандартизация — не магический ритуал, делающий язык лучше

                                                                                                                                  это «магический ритуал», который делает компиляторы языка лучше и однообразнее.
                                                                                                                                    +2
                                                                                                                                    implementation-defined отличается от undefined behavior только тем, что первое компилятор обязан как-нибудь реализовать, а второе — нет. Однако это не значит, что компилятор обязан оставить поведение неопределенным. Например, «положить в union значение одного типа а достать — другого» это UB по букве стандарта. Однако, такое поведение определено во всех основных компиляторах одинаково и повсеместно используется.

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


                                                                                                                                    Но это как-то ну так себе.

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

                                                                                                                                  Не понял, почему?


                                                                                                                                  Другое дело, что в общем случае разрешение UB в плюсах — undecidable в смысле теории алгоритмов, поэтому в общем случае до момента выполнения неизвестно, UB там или нет.

                                                                                                                                  0

                                                                                                                                  Да ладно отмазываться. Я как Rust разработчик, страдаю от отсутствия стандарта. Приходится порождать вопросы типа таких: https://github.com/rust-lang-nursery/reference/issues/485

                                                                                                                                    0
                                                                                                                                    Отмазываться? От чего? Штампика ISO на The Rust Reference нет, он неполон, но говорить, что в Rust'e всё — implementation defined неправильно.

                                                                                                                                    > порождать вопросы типа таких

                                                                                                                                    Хех. Я был бы очень удивлён, если бы Rust исправлял UB в C-библиотеке.
                                                                                                                                      0
                                                                                                                                        0

                                                                                                                                        Хм, если бы Rust менял правила работы с i8 в зависимости от того из какого языка вызывается библиотека, это было бы очень странно, если возможно.


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

                                                                                                                              0
                                                                                                                              Корректность ПО — его способность реализовывать требуемый алгоритм в определенных условиях. Ни больше, ни меньше. Полагаться на документированное поведение компилятора — ваше право (так же, как и в расте). Стандарт лишь описывает требования к компиляторам.

                                                                                                                              Но это уже не будет кодом на C++.


                                                                                                                              Если мы в итоге нашего спора придём к выводу, что кода на C++ не существует, это, в принципе, тоже будет любопытным заключением.

                                                                                                                            +5
                                                                                                                            Компилятор имеет право корректно обрабатывать UB не нарушая при этом стандарт.

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


                                                                                                                            Компилятор, который при


                                                                                                                            int arr[5];
                                                                                                                            auto ptr = arr + 6;

                                                                                                                            отформатирует вам жёсткий диск, будет показывать абсолютно корректное поведение. Да, формирование указателя за пределы элемента-следующего-за-последним-элементом-массива — UB, даже если вы его не разыменовываете.


                                                                                                                            И, кстати, количество кода, которое формирует указатели за пределы массивов, я пересчитать не могу. Это в том или ином виде было в каждом втором проекте (и в 9 проектах из 10, где вообще была хоть какая-то адресная арифметика хотя бы в виде arr[i]).


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


                                                                                                                            Что как бы намекает на распространённую ментальность сообщества.

                                                                                                                              0
                                                                                                                              Отвечаю сразу на 1, 2, 3, 4.

                                                                                                                              Погодите, еще раз. Вот у нас ситуация: «программист на языке A полагается не на стандарт, а на поведение конкретного компилятора, описанное в документации к этому компилятору».

                                                                                                                              Если A = «раст», то под неё подходит абсолютно весь код, и вы считаете, что это нормально.

                                                                                                                              Но если вдруг A = «c++», то вы орете как это плохо, «UB», «это даже не с++», «плохая распространенная ментальность общества» и прочее. И это только потому, что в с++ (в отличие от раста) есть настоящий документ, по-настоящему описывающий его поведение? На мой взгляд, вы сейчас демонстрируете запредельное лицемерие. И вам за него еще лайки ставят. Я в шоке
                                                                                                                                0
                                                                                                                                Ну, во-первых, я ничего не говорил про раст. Я и не фанат раста так-то, и не знаю его, и не вижу, куда его мне засунуть в моих задачах.

                                                                                                                                Во-вторых, мы говорили о коде на C++. Если программист пишет для конкретного компилятора с учётом конкретного способа обработки конкретного UB этим компилятором, то
                                                                                                                                1. Это не код на C++. В коде на C++ не бывает UB, вообще, никогда, никак. Это код на некотором диалекте, понимаемом gcc. Или MSVC. Или clang.
                                                                                                                                2. Успехов этому программисту с портированием его софтины на другие платформы или даже хотя бы с обновлением компилятора.
                                                                                                                                3. Успехов этой фирме с наймом, например, меня, потому что я в гробу видал сегодня писать, скажем, на gcc 4.2 или даже 5.3 (редхатовский DTS-4), или через 5-10 лет — на gcc 8.2. Хотя тут, наверное, фирме даже повезло.
                                                                                                                                  0

                                                                                                                                  Вы не понимаете, что есть поведение "стандартное" (не важно, чем оно определено: действительным формальным стандартом или даже просто договоренностью) и "нестандартное", то есть известное ошибочное поведение.


                                                                                                                                  При этом, если вдруг UB возникает в "стандартной" части программы, то это — ошибка компилятора. Но если оно возникает в "нестандартной" — то это считается нормой. Ибо за пределами "стандарта" компилятор снимает с себя ответственность за что бы то ни было.


                                                                                                                                  В Rust есть явная, четкая граница между этими двумя мирами, в C++ — такой нет.


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


                                                                                                                                  Но меняет ли это ситуацию принципиально? Является ли GCC C++ — действительно безопасной версией C++, лишенной UB или явно ограничивающей его от остального кода? Только это обстоятельство принципиально может отличить безопасность GCC C++ от C++ как такового.

                                                                                                                                    0
                                                                                                                                    Вы не понимаете, что есть поведение «стандартное» и «нестандартное», то есть известное ошибочное поведение.

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

                                                                                                                                    не важно, чем оно определено: действительным формальным стандартом или даже просто договоренностью

                                                                                                                                    Вот решил я, например, сделать rust компилятор. Распечатайте мне, пожалуйста, эту устную договоренность.

                                                                                                                                    то да, корректнее будет сравнивать тогда не C++ vs. Rust, а GCC C++ vs. Rust

                                                                                                                                    Вы хотели сказать GCC C++ vs. Mozilla Rust?

                                                                                                                                    если такие дополнительные гарании компилятор действительно дает, а не просто мы полагаемся на текущую его реализацию

                                                                                                                                    С точки зрения языка, «реализация» и «компилятор» — синонимы. Да, компилятор берется гарантировать некоторые инварианты при использовании этого компилятора. Но не более

                                                                                                                                    Но меняет ли это ситуацию принципиально?

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

                                                                                                                                    Является ли GCC C++ — действительно безопасной версией C++, лишенной UB или явно ограничивающей его от остального кода?

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