Современный C++ нас не спасет

Автор оригинала: Alex Gaynor
  • Перевод

Я часто критикую небезопасные при работе с памятью языки, в основном C и C++, и то, как они провоцируют необычайное количество уязвимостей безопасности. Моё резюме, основанное на изучении доказательств из многочисленных крупных программных проектов на С и С++, заключается в том, что нам необходимо мигрировать нашу индустрию на безопасные для памяти языки по умолчанию (такие как Rust и Swift). Один из ответов, который я часто получаю, заключается в том, что проблема не в самих С и С++, разработчики просто неправильно их "готовят". В частности, я часто получаю в защиту C++ ответ типа: "C++ безопасен, если вы не используете унаследованную от C функциональность" [1] или аналогичный ему, что если вы используете типы и идиомы современного C++, то вы будете застрахованы от уязвимостей типа повреждения памяти, от которых страдают другие проекты.

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


Скрытая ссылка и use-after-free

#include <iostream>
#include <string>
#include <string_view>

int main() {
  std::string s = "Hellooooooooooooooo ";
  std::string_view sv = s + "World\n";
  std::cout << sv;
}

Вот что здесь происходит, s + "World\n" создает новую строку std::string, а затем преобразует ее в std::string_view. На этом этапе временная std::string освобождается, но sv все еще указывает на память, которая ранее ей принадлежала. Любое последующее использование sv является use-after-free уязвимостью. Упс! В С++ не хватает средств, чтобы компилятор знал, что sv захватывает ссылку на что-то, где ссылка живет дольше, чем донор. Эта же проблема затрагивает std::span, также чрезвычайно современный тип С++.

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

#include <memory>
#include <iostream>
#include <functional>


std::function<int(void)> f(std::shared_ptr<int> x) {
    return [&]() { return *x; };
}

int main() {
    std::function<int(void)> y(nullptr);
    {
        std::shared_ptr<int> x(std::make_shared<int>(4));
        y = f(x);
    }
    std::cout << y() << std::endl;
}

Здесь [&] в f лямбда захватывает значение по ссылке. Затем в main, x выходит за пределы области видимости, уничтожая последнюю ссылку на данные и освобождая их. В этот момент y содержит висячий указатель. Это происходит, несмотря на наше тщательное использование умных указателей. И да, люди действительно пишут код, использующий std::shared_ptr&, часто как попытку избежать дополнительного приращения и уменьшения количеств в подсчитывающих ссылках.

Разыменование std::optional

std::optional представляет собой значение, которое может присутствовать, а может и не присутствовать, часто заменяя магические значения (например, -1 или nullptr). Он предлагает такие методы, как value(), которые извлекают T, которое он содержит, и вызывает исключение, если optional пуст. Однако, он также определяет operator* и operator->. Эти методы также обеспечивают доступ к хранимому T, однако они не проверяют, содержит ли optional значение или нет.

Следующий код, например, просто возвращает неинициализированное значение:

#include <optional>

int f() {
    std::optional<int> x(std::nullopt);
    return *x;
}

Если вы используете std::optional в качестве замены nullptr, это может привести к еще более серьезным проблемам! Разыменование nullptr дает segfault (что не является проблемой безопасности, кроме как в старых ядрах). Однако, разыменование nullopt дает вам неинициализированное значение в качестве указателя, что может быть серьезной проблемой с точки зрения безопасности. Хотя T* также бывает с неинициализированным значением, это гораздо менее распространено, чем разыменование указателя, который был правильно инициализирован nullptr.

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

#include <optional>
#include <memory>

std::unique_ptr<int> f() {
    std::optional<std::unique_ptr<int>> x(std::nullopt);
    return std::move(*x);
}

Индексация std::span

std::span обеспечивает эргономичный способ передачи ссылки на непрерывный кусок памяти вместе с длиной. Это позволяет легко писать код, который работает с несколькими различными типами; std::span может указывать на память, принадлежащую std::vector, std::array<uint8_t, N> или даже на сырой указатель. Некорректная проверка границ - частый источник уязвимостей безопасности, и во многих смыслах span помогает, гарантируя, что у вас всегда будет под рукой длина.

Как и все структуры данных STL, метод span::operator[] не выполняет проверку границ. Это печально, так как operator[] является наиболее эргономичным и стандартным способом использования структур данных. std::vector и std::array можно, по крайней мере, теоретически безопасно использовать, так как они предлагают метод at(), который проверяет границы (на практике я этого никогда не видел, но можно представить себе проект, использующий инструмент статического анализа, который просто запрещает вызовы std::vector::operator[]). span не предлагает метод at(), или любой другой подобный метод, который выполняет проверку границ.

Интересно, что как Firefox, так и Chromium в бэкпортах std::span выполняют проверку границ в operator[], и, следовательно, никогда не смогут безопасно мигрировать на std::span.

Заключение

Идиомы современного C++ вводят много изменений, которые могут улучшить безопасность: умные указатели лучше выражают ожидаемое время жизни, std::span гарантирует, что у вас всегда под рукой правильная длина, std::variant обеспечивает более безопасную абстракцию для union. Однако современный C++ также вводит новые невообразимые источники уязвимостей: захват лямбд с эффектом use-after-free, неинициализированные optional и не проверяющие границы span.

Мой профессиональный опыт написания относительно современного С++ кода и аудита Rust-кода (включая Rust-код, который существенно использует unsafe) заключается в том, что безопасность современного С++ просто не сравнится с языками, в который безопасность памяти включена по умолчанию, такими как Rust и Swift (или Python и Javascript, хотя я в реальности редко встречаю программы, для который имеет смысл выбора - писать их на Python, либо на C++).

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

[1] Это надо понимать, что речь идет о сырых указателях, массивах-как-указателях, ручном malloc/free и другом подобном функционале Си. Однако, думаю, стоит признать, что, учитывая, что Си++ явно включил в свою спецификацию Си, на практике большинство Си++-кода содержит некоторые из этих "фич".

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

Подробнее

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

    +7
    Пример с std::optional просто фееричен по уровню идиотизма. Тип optional и не предоставляет гарантии, что он всегда содержит какое-то значение. На это даже намекает само название типа. Претензии к операторам * и -> только показывают, что автор понятия не имеет о контрактах функций. Если мы посмотрим описание этих операторов на cppreference, то мы увидим, что
    The behavior is undefined if *this does not contain a value.

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

    Хотя я не отрицаю, что современный C++ превратился в какого-то монстра и использовать его корректно очень и очень сложно, чтобы бросаться переносить существующие наработки, надо сначала создать хорошую замену C++. Языки с VM и сборщиком мусора не являются заменой по определению. Rust выглядит довольно перспективно, но тоже не без проблем.
      +12
      Разработчик обязан удовлетворить предусловия функции перед ее вызовом.

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


      И насчёт "нацеленности на максимальную производительность" тоже, вроде как, справедливо. Но если быть честным, то насколько замедлится софт написанный на С++, если там включить проверки в std::option и векторе? Гадать дело неблагодарное, но я что-то думаю, что далеко не везде это будет заметно.


      В этом плане мне намного больше нравится подход раста, где по умолчанию такие проверки как раз есть. А если вдруг мы видим, что в этом конкретном месте упираемся в производительность, то всегда можно использовать "unchecked" версию соответствующего метода (и желательно обложиться тестами). Да, в С++ есть at, но честно говоря не встречал чтобы им активно пользовались. Может мне не повезло, конечно. Но зачастую при выборе между доступом по индексу через квадратные скобки и "неуклюжим" вызовом get_unchecked выберут первое. В общем, я за "правильные" умолчания.

        +3
        Как правило в реализации std::optional стоит assert(), который срабатывает в debug билдах.
          0
          Но вот у клиента и тестировщика как правило билд не debug. Поэтому assert «никто» не увидит
            0
            Ну, с вероятностью 99.99% это всё равно приведёт к аварийному завершению программы. А если есть Дося, то зачем платить больше.
              +3

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

          0
          Логично, но люди совершают и будут совершать ошибки
          то то я смотрю в безопасных питонах и жсах люди совсем никогда не допускают ошибок…
          И насчёт «нацеленности на максимальную производительность» тоже, вроде как, справедливо. Но если быть честным, то насколько замедлится софт написанный на С++, если там включить проверки в std::option и векторе?
          тут не может быть «на полшишечки», либо яп нацелен на максимальную производительность, либо нет.
          Да, в С++ есть at, но честно говоря не встречал чтобы им активно пользовались. Может мне не повезло, конечно
          вам не повезло
            +4
            то то я смотрю в безопасных питонах и жсах люди совсем никогда не допускают ошибок…
            Ошибок управления памятью всё же не совершают
          +6
          Претензии к операторам * и -> только показывают, что автор понятия не имеет о контрактах функций.

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


          Не все предусловия можно проверить.

          Зависит от языка.


          Не все проверки предусловий осуществимы за разумное время.

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

            +3
            Зависит от языка.

            Я конечно далеко не эксперт в разных языках и парадигмах программирования, но тем не менее с этим утверждением я не согласен. Предусловия бывают самые разные, явные и неявные (подразумевающиеся). Как вы проверите, что вам передали указатель на последовательность символов, оканчивающуюся нулем? Если вызывающий код забудет дописать терминирующий ноль, вы этот факт никак не установите. Как вы проверите, что переданный в функцию указатель на структуру действительно указывает на такую структуру? Rust может гарантировать нечто подобное, но если данные пришли из внешнего источника их все равно придется валидировать. Любой самый защищенный язык при работе с низкоуровневыми абстракциями будет вынужден использовать unsafe секции кода. И там будут непроверяемые или сложнопроверяемые предусловия.
              +3
              Как вы проверите, что вам передали указатель на последовательность символов, оканчивающуюся нулем?

              введя соответсвующий тип, и имея / владея всеми источниками в момент компиляции — чтобы компилятор вывел что мы соблюдает его/свои правила
                0
                А если данные пришли извне? Если вам просто был передан указатель на область памяти, каковы будут ваши действия, чтобы осуществить данную проверку?
                  +4

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

                    0

                    А теперь убираем из этой схемы завтипы и ничего не меняется :)

                      +3

                      Только без завтипов, если я уберу проверку, компилятор и не пикнет, а вот с ними…


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

                        0

                        А зачем вы уберете проверку?

                          0

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

                            –2

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

                              +2

                              Почему странно? Нажал Vd Ctrl+X, чтобы проверку в другое место перенести, и отвлекся. Или поменял код в друом месте, который теперь стал требовать сильно больше проверок.


                              Мне типы и иерархии классов приходилось распутывать, но обычно это было в плюсах и от фанатов паттернов.

                                0

                                Не всегда только паттернами можно испортить код

                              –1

                              Если уж так волнуетесь за сохранность проверки при оефакторинге, пишете тест до рефакторинга и запускаете после

                                +1

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

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

                    Потребую доказательство, что последний элемент массива — ноль. Это как раз просто.


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

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


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


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

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


                    Но вообще общение с внешним миром — это совсем другая и довольно история. Однако, обсуждаемый вопрос об operator* у std::optional к общению со внешним миром отношения не имеет.

                      0
                      Потребую доказательство, что последний элемент массива — ноль. Это как раз просто.
                      А почему именно последний? Очень вероятно, что массив не будет заполнен полностью и ноль окажется где-то в середине, например — получение пакета по сети, обмен не обязятельно ведётся по 1500 байт.
                      Однако, обсуждаемый вопрос об operator* у std::optional к общению со внешним миром отношения не имеет.
                      Запросто, например, получение сетевого пакета без задержек, ситуацию моделирую тут, но почему бы и нет
                      std::optional<std::string> recv_str = server.recv();
                      if( recv_str ) { ...
                      } else { ...
                      }
                        +4
                        А почему именно последний? Очень вероятно, что массив не будет заполнен полностью и ноль окажется где-то в середине, например — получение пакета по сети, обмен не обязятельно ведётся по 1500 байт.

                        Спросили про последний — я ответил про последний.


                        А зачем требовать наличие нулевого байта в пакете, полученном по сети, мне не очень понятно.


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

                        Вот там, где вы делаете if (recv_str), и где вы выбираете первую ветвь, вы на самом деле неявно имеете доказательство того, что optional у вас непустой.


                        Более того, здесь никаких завтипов не нужно. Смотрите, как это было бы выражено пусть даже в хаскеле:


                        recvStr <- recv server
                        case recvStr of
                             Just str -> ...
                             Nothing -> ...

                        У вас здесь проверка наличия содержимого и его извлечение совмещены в одну операцию. Никакого небезопасного operator* заведомо не нужно.

                  +1

                  Это пример неудачно разработанного API: часто используемая функция, могущая вызвать UB при случайном неправильном применении. Очень похожий тип есть в Rust и называется Option, но там он свободен от подобной проблемы: у него нет оператора разыменования. Есть либо извлечение с явной проверкой:


                  let x: Option<i32> = None;
                  if let Some(y) = x {

                  либо преобразование Option в другой Option через map:


                  let y = x.map(|v| v+1);

                  либо "разыменование", но с паникой (в C++ это могло бы быть исключение) в случае его некорректности:


                  let x: Option<i32> = None;
                  let y = x.expect("Bug: x contains no value");

                  Такой API гарантирует, что при некорректном использовании будет не UB, а всего лишь корректная runtime error. Сама вероятность некорректного использования также понижается.

                    +1

                    Меня удивляет, что у std::optional в C++ нет ничего похожего на Option::map/Option::and_then. Вроде ж максимально логичные методы.

                      0
                      Для монадического интерфейса optional есть proposal, но да, это все идет довольно медленно.
                        0
                        Есть в бустовском
                        0

                        В расте может быть unsafe unwrap_unchecked()

                          +2

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

                        –4

                        Сравните тот же Option из раста. Оно гарантирует либо панику, либо значение. В C++ в лучшем случае будет исключение, в худшем кто-то получит доступ к вашей памяти. То бишь не гарантирует практически ничего, кроме почти безопасного интерфейса. Об это и ведет речь автор. Пока не заведутся полноценные контракты из нового стандарта безопасность приложений останется под угрозой эксплуатирования таких проблем с памятью. Но и контракты не панацея и ждать их в лучшем случае лет 5.

                        –3
                        std::string_view это вообще невладеющая обёртка над голым указателем и не может гарантировать время жизни данных. Автор олигофрен. Раст не может рассматриваться как замена плюсов из-за своих убогих архитектурных возможностей. Пока раст даже не может быть рассмотрен как системный язык — замена си. См. недавние хороводы вокруг драйверов ядра линукс.
                          +10
                          Автор всего лишь показал, что человеки не способны программировать правильно, если компилятор за такие вещи не даёт по рукам. Была как-то инфографика выявления уязвимостей в проекте Chromium, так выходило, что каждые два дня находят одну уязвимость уже в течение 10 лет.
                            +7
                            Автор предлагает запретить использование ножей и вилок потому что ими можно порезаться/уколоться?

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

                            В чистом С все было честно — вот вам очень острый ножик, им можно делать что угодно, но легко порезаться если делать это без ума. В С++ ножик по прежнему остр, но эта острота тщательно скрыта от разработчика.
                              +3
                              Мда, за предложение сделать без уязвимостей уже в карму срут…
                              Предлагается не связывать ножи и вилки вместе в разные стороны. А так же не снимать с лезвия рукоятку.
                                +4
                                Не «без уязвимостей», а провоцирующие стиль «херак-херак и в продакшн».
                                  +7
                                  Мда, за предложение сделать без уязвимостей

                                  Сложные проблемы всегда имеют простые, легкие для понимания неправильные решения (С) сборник законов Мерфи, если не ошибаюсь. Впрочем, очередная статья про внезапный «нибизапастный сиплюсплюс», не знаю, стоило ли ее переводить — разве что в качестве затравки для очередного срача.
                                    +2
                                    Да собственно, у меня мнение тоже совпадало с «не умеете готовить» из 1го абзаца, потому я начал читать статью. А что читать, что переводить почти одинаково, да и я подумал что не один такой.

                                    Да и когда кому мешал хороший технический срач? =)
                                      +1
                                      Так ведь статистика говорит сама за себя. Почитать хотя бы тот же Opennet — ни дня без закрытия найденных уязвимостей переполнения стэка или use after free.
                                        +8
                                        Еще статистика говорит, что в open source коде, написанном на php, java, и js уязвимостей находят больше (а на python — примерно столько же), чем в коде на C++ (но меньше, чем на C, главным образом по причинам «исторического характера» — да и то, если взять в сумме C/C++ и эти языки, то получается сравнимо). Только там они другие — xss всякие, input validation, information disclosure. Уязвимости другие, результат тот же. Надо срочно менять и эти языки на что-то другое? А поможет ли это? Что-то я сомневаюсь. Да и авторы исследования тоже приходят к закономерно банальному выводу, что «it is not about the language itself that makes it any more or less secure, but how you use it».

                                        На rustsec загляните тоже между делом — при всей мизерности объемов существующего кода на расте уязвимости типа double-free/use-after-free и прочий букет там находят регулярно, в том числе связанные с нарушением контрактов. Это не сферический «всегда-safe-код-в-вакууме» из мечт апологетов раста, а то, что обычный средний разработчик будет писать на расте в реальности. А вы, извините, как филин из известного анекдота со стратежным советом «надо, чтобы компилятор думал за человека, тогда все будет хорошо». Компиляторы тоже пишут люди.

                                        Ладно, этот срач уже не первый (и по-видимому не последний), все аргументы всех сторон известны наперед, не вижу смысла в дальнейшем участии в очередной итерации :) Займусь чем-нибудь более полезным, поправлю вон очередной баг в fheroes2.
                                          0
                                          Ну да, когда нам говорят, что у нас что-то плохо, давайте показывать пальцев на других :))) (Почему-то кроме Раста)
                                          «it is not about the language itself that makes it any more or less secure, but how you use it»
                                          Ну так Раст как раз и заточен на то, чтобы не было зависимости от but how you use it.
                                            +2
                                            Надо срочно менять и эти языки на что-то другое? А поможет ли это? Что-то я сомневаюсь.
                                            Вообще-то да. Например, php в поставке по умолчанию, уязвим к загрузке файлов на сервер, и для обхода врождённой проблемы могут потребоваться значительные усилия, так-как сам язык подразумевает вызов кода по имени файла напрямую.

                                            XSS возможен, так как языки работают с ответом как со строкой, безо всякой проверки корректности. Будь ответ представлен не как строковой буфер, а как дерево, пускай даже с лёгкой возможность вставки сырых строк, то это уже существенно бы облегчило проверку кода, и снизило количество ошибок. В случае, если бы для использования сырых строк дополнительно проверялось бы компилятором, как например в rust, то эти ошибки стали бы ещё более редкими, так как новички не смогли бы элементарно пройти проверку валидации. Например, следующий код не скомпилируется
                                            fn main() {
                                                let t = "Hello, world!";
                                                println!(t);
                                            }
                                              +1
                                              Это не сферический «всегда-safe-код-в-вакууме» из мечт апологетов раста, а то, что обычный средний разработчик будет писать на расте в реальности.

                                              #![forbid(unsafe_code)] в корне проекта — и у вас в команде никто unsafe-ом дров не наломает.

                                                –1
                                                вот сижу я, пишу библиотеку с кучей требований по перфу и памяти, приходится использовать всякие ухищрения типа буфера + спанов в него, на расте без unsafe я такое в принципе бы не смог написать. Сидя в пустыне дров не наломаешь, да…
                                                  0
                                                  приходится использовать всякие ухищрения типа буфера + спанов в него, на расте без unsafe я такое в принципе бы не смог написать.

                                                  Так, а вот про это можно поподробнее?

                                                    +2
                                                    Так, а вот про это можно поподробнее?
                                                    ну в плюсах я спокойно могу написать и использовать что-то в духе такого минимального примера:
                                                    код
                                                    struct Foo {
                                                        Foo(std::string s) :
                                                            str(std::move(s)),
                                                            view(str) // ссылка на тот же буфер, что и в str
                                                        {}
                                                    
                                                        std::string str;
                                                        std::string_view view;
                                                    };

                                                    Проблема описана тут. Самое забавное, что в Rust std::String не имеет SSO оптимизации, и по идее слайс должен быть корректен даже после мува.
                                                      –2

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

                                                        +2
                                                        Как бы с одной стороны да, с другой — я бы на ревью подобный код не пропустил бы, поскольку тут view очень легко может повиснуть.
                                                        А с какой целью вы ревьюите код? Проверить его на наличие ошибок или завернуть по подозрению, которое вам лень перепроверять?
                                                          +1
                                                          Проверить его на наличие ошибок или завернуть по подозрению, которое вам лень перепроверять?

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


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

                                                            –1

                                                            Мне лень высматривать весь код, чтобы убедиться в том, что в нём нет операций, которые меняют str, и я глубоко сомневаюсь, что люди, которые с этим кодом будут работать в дальнейшем, будут помнить, что это самоссылающаяся структура. Даже просто поменять тип str на const std::string было бы лучше.


                                                            И, кстати, конкретно в этом коде я вообще не вижу смысла в поле view. Оно и так будет инвалидировано при любом операции, затрагивающей str, а создать string_view из std::string можно в любой момент, это очень дешёвая операция. Так зачем его вообще хранить?

                                                              +1
                                                              Мне лень высматривать весь код
                                                              представьте что вы реализовали какую-то нетривиальную функциональность, выложили код на ревью, и получаете от коллеги отзыв «я не вчитывался конечно, но мне кажется что ты где-то мог накосячить, переделай». Понравится?
                                                              Даже просто поменять тип str на const std::string было бы лучше.
                                                              ну я так и сделал по итогу
                                                              И, кстати, конкретно в этом коде я вообще не вижу смысла в поле view
                                                              ну это же сильно упрощенный пример, в реальном коде куча вьюшек на разные диапазоны внутри буфера.
                                                                0
                                                                Понравится?

                                                                Да.


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

                                                                  +2
                                                                  И я считаю, что это делает мой код (и статью) лучше, чище и понятнее.
                                                                  логично, ведь задача ревью как раз улучшить код, который попадет в базу. Вот замечания с предложениями (нераздельно!) эту цель преследуют. Или даже вопросы, иногда они действительно указывают на неочевидные моменты. А вот задачи «не допустить код» у код ревью нет и никогда не будет — код ведь с какой-то целью пишется, верно?

                                                                  И когда я вижу комментарии а-ля «я бы на ревью подобный код не пропустил бы» и «мне лень высматривать весь код, чтобы убедиться...» я делаю вывод, что у человека синдром вахтера и ЧСВ.
                                                                    –1
                                                                    И когда я вижу комментарии а-ля «я бы на ревью подобный код не пропустил бы» и «мне лень высматривать весь код, чтобы убедиться...» я делаю вывод, что у человека синдром вахтера и ЧСВ.

                                                                    То есть недопущение в кодовую базу кода, корректное использование которого требует нелокального соблюдения важного инварианта, который нельзя зафорсить компилятором и потому почти гарантированно создаст проблемы при сопровождении — это ЧСВ?

                                                                      +2
                                                                      То есть недопущение в кодовую базу кода,… — это ЧСВ?
                                                                      нет, это синдром вахтера. ЧСВ вы проявили в «мне лень проверять, я проще заверну» — я не могу воспринимать это иначе как неуважение к коллегам.
                                                                      корректное использование которого требует нелокального соблюдения важного инварианта
                                                                      это вы из головы взяли, у меня вся логика под капотом весьма простого API.
                                                                      почти гарантированно создаст проблемы при сопровождении
                                                                      любой нетривиальный код, решающий нетривиальную задачу, вызовет проблемы при сопровождении, если разработчики не будут даже пытаться разобраться в коде. Напомню, что наша дискуссия началась с фразы «пишу библиотеку с кучей требований по перфу и памяти, приходится использовать всякие ухищрения».
                                          +8
                                          Автор предлагает запретить использование ножей и вилок потому что ими можно порезаться/уколоться?

                                          Интересный выбор аналогий.


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


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

                                          Да, ошибок use-after-free, переполнений буфера и тому подобного в коде на С не бывает. И в линуксе такого не было, и в openssl не было, и нигде не было.

                                            +1

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

                                              +7

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

                                                0

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

                                                  +2

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

                                                    +3

                                                    Ну тут он зря. ИМХО раст — небезопасный и слабый язык, но для инициирования дискуссии полезен.

                                                      0

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


                                                      Кстати, что есть мощное и безопасное без сборки мусора? Agda и ATS? Да, где-то применяются, но даже относительно раста этого не особо видно. Может, конечно, не там смотрю. Так-то я надеюсь, что индустрия будет двигаться в эту сторону, но пока это всё весьма далеко от мейнстрима.

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

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


                                                        Кстати, что есть мощное и безопасное без сборки мусора? Agda и ATS?

                                                        Про ATS не знаю, а в агде точно сборка мусора есть.

                                                          0
                                                          Про ATS не знаю, а в агде точно сборка мусора есть.

                                                          Да, это я "немного" с адой перепутал. Но всё-таки: получается ничего особо и нет без сборки мусора?..

                                                            +1

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

                                                          0
                                                          Кстати, что есть мощное и безопасное без сборки мусора? Agda и ATS?
                                                          Навскидку, Ada, BetterC, Swift, Zig.

                                                          Раст я списал в утиль. Металлолом, чё.
                                                            0
                                                            Ada

                                                            Бесполезна без SPARC. И да, какой там профиль Ravenscar выбирать?


                                                            BetterC

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


                                                            Swift

                                                            Который вставляет подсчёт ссылок для каждого класса? Не смешите меня.


                                                            Zig

                                                            Который базируется на принципе "программист знает, чё делает", так же, как и C.

                                                              0
                                                              Экий Вы максималист =)
                                                              Я ответил на конкретно заданный вопрос.

                                                              Кстати, есть статья, в которой анализируется надежность Zig в сравнении с Rust. Небольшое преимущество как по мне.

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

                                                              Из перечисленного под критерий "мощное и безопасное" подходит только ада. Свифт — приятный язык, но в чём его мощь? Да и безусловный подсчёт ссылок действительно не всегда подходит для системного (а иначем зачем от сборки мусора отказываться?) языка.


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


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


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

                                                              Объективно, ничего не скажешь.

                                                                0
                                                                Это другая статья про Zig, не та что я переводил.
                                                                Ну и начинать проект на D или Zig я бы побоялся.
                                                                Я бы тоже. Конкретнее, я даже С++11 побоялся в своей конторе — типичные люди не тянут, выбрал С#. Через 13лет видны конечно и недостатки такого выбора.

                                                                Объективно, ничего не скажешь.
                                                                Раст не годится для энтерпрайза — птичий, сложный и неудобный язык. Надеюсь, найдет свою нишу. Где-нибудь в драйверах.
                                                                  +2
                                                                  Раст не годится для энтерпрайза — птичий, сложный и неудобный язык.

                                                                  С "неудобный" я бы поспорил. Ни в одном мейнстримном ЯП нету сумм-типов. О каком удобстве вообще может идти речь?

                                                                    +1

                                                                    Да и со сложный я бы поспорил. Относительно какого-нибудь c++11-20 довольно легкий язык.

                                                                    0
                                                                    Это другая статья про Zig, не та что я переводил.

                                                                    Да нет, мне именно статья по ссылке попадалась. Правда я её скорее пролистал, так что от перевода не отказался бы. (:


                                                                    выбрал С#. Через 13лет видны конечно и недостатки такого выбора.

                                                                    Было бы весьма любопытно послушать подробности.


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

                                                                    Что ж, могу порадоваться, что я не энтерпрайзом занимаюсь.

                                                              –1

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

                                                                0
                                                                небезопасный и слабый по сравнению с чем? С Idris? А с языками из условного top20 github?
                                                                +2
                                                                Хорошо придумывать аналогии — они подобны котёнку с дверцей. Безопасных языков много, просто некоторые из них не снискали популярности, на некоторые нужно переучиваться. Кроме описанных в текущей статье, можно вспомнить как минимум этот список, и он ведь тоже не полный.
                                                                0

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

                                                                  0

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

                                                                    –1

                                                                    У встроенных проверок есть одна неприятная особенность. Они могут неудовлетворять имеющейся инфраструктуре. Например, есть метод at, который бросает исключение. Но какое исключение? Есть ли в этом исключении нормальное описание проблемы? Stacktrace? А проект все это требует

                                                                      +1

                                                                      То есть громкое падение (я уже не говорю об UB) инфраструктуре удовлетворяет, а исключения (которые можно перехватить и обработать должным образом) нет?

                                                                        0

                                                                        Когда я смотрю на исключение, выванное at, я понимаю, что лучше бы он упал с нормальным core dump-ом.


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

                                                                +2
                                                                Не уверен, что аналогия удачная. Гонщик в гоночном болиде защищён заметно лучше, чем водитель в обычном автомобиле.
                                                                  –2

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

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


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

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

                                                                Да, ошибок use-after-free, переполнений буфера и тому подобного в коде на С не бывает. И в линуксе такого не было, и в openssl не было, и нигде не было.


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

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

                                                                Это и есть стиль «херак и в продакшен». Быстро и эффективно. А там трава не расти.
                                                                  +7
                                                                  Речь идет именно об инструментарии для создании чего-то нового, а не о конечном потребительском продукте.

                                                                  И что новое вы создаёте ножом и вилкой?


                                                                  Но это было следствие невнимательности или низкой квалификации разработчика.

                                                                  Где же эти разработчики с высокой квалификацией, над чем они работают? Точно не над openssl, точно не над ядром линукса, и даже всякие гуглы-майкрософты их не могут нанять. Где же они?


                                                                  Т.е. от разработчика требовалась некоторая квалификация, а не просто прослушать пару бесплатных курсов на трубе.

                                                                  Не люблю такую аргументацию, но позволю себе. Так вот, примерно за 17 лет писания кода на плюсах, из которых лет 15 я получал за это деньги, из которых лет 10 — фуллтайм, я убедился, что на плюсах (и на С) писать безопасный и надёжный код в программах сложнее хелловорлда за адекватное время невозможно.


                                                                  А автор предлагает некий «язык для даунов»

                                                                  И снова интересная интерпретация. Даже попахивает каким-то элитизмом.


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


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

                                                                  Олсо, сторонники C предлагают некий «язык для даунов», который сам аллоцирует регистры, сам настроит стекфрейм…

                                                                    –1
                                                                    Точно не над openssl, точно не над ядром линукса

                                                                    Как вы лихо подменили отрицание квантора всеобщности на всеобщность отрицания

                                                                      +1

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

                                                                        –1

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

                                                                          +4

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

                                                                            –1

                                                                            Дык я вроде и не хвалю. C++ — ужасный язык

                                                                              +1
                                                                              Ну так зачем хвалить С++, аппелируя к тому, как он хорош в руках программистов, которых не бывает?
                                                                              странная логика. Крупнейшие современные софтовые проекты написаны на плюсах и на си (браузеры, компиляторы, виртуальные машины, ОСи...), и пара багов помножает на ноль квалификацию их авторов? То есть если я зайду в багтрекер какого-нибудь rust-проекта и увижу там некоторое количество тикетов, я тоже могу сказать что на нём невозможно разрабатывать, видите ли потому что баги бывают?
                                                                                +2
                                                                                Крупнейшие современные софтовые проекты написаны на плюсах и на си (браузеры, компиляторы, виртуальные машины, ОСи...), и пара багов помножает на ноль квалификацию их авторов?

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


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

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

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

                                                                                    Одно другому не противоречит. Топовые программисты на C++ всё равно могут допускать (и допускают) ошибки в значимом количестве.


                                                                                    А как раст, так никто никогда в unsafe не ошибается, или я не понимаю и это другое?

                                                                                    Да ё-моё. Не топлю я за раст.

                                                                                      –1
                                                                                      Одно другому не противоречит. Топовые программисты на C++ всё равно могут допускать (и допускают) ошибки в значимом количестве
                                                                                      а «в значительном количестве» это сколько? Вот например взять среднего питониста и среднего плюсовика, замерить errors/kLOC, и какие числа получатся? Просто если вы так уверены в достоверности своих тэзисов, наверняка у вас есть эксперименты, способные их подкрепить? В конце концов, должно же быть что-то объективнее чем «а вон в хроме по ошибке в пару дней», учитывая что там дифф/час измеряется тысячами строк.
                                                                                        +1
                                                                                        Просто если вы так уверены в достоверности своих тэзисов, наверняка у вас есть эксперименты, способные их подкрепить?

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

                                                                                      0
                                                                                      А как раст, так никто никогда в unsafe не ошибается, или я не понимаю и это другое?

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

                                                                                +1

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

                                                                                  0

                                                                                  Ну, если сможете показать как и чем это поможет, то будет круто. Вопрос то в простоте и массовости. И на c++ можно нормально писать, если знать как

                                                                                    +1
                                                                                    Ну, если сможете показать как и чем это поможет, то будет круто.

                                                                                    Вы о каком-то конкретном примере?


                                                                                    Вопрос то в простоте и массовости.

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


                                                                                    И на c++ можно нормально писать, если знать как

                                                                                    Просто мало кто знает.


                                                                                    Ну и если эта самая внимательность у вас бесконечная.

                                                                                      –1
                                                                                      Вы о каком-то конкретном примере?

                                                                                      Я о том как писать код собственно.


                                                                                      У меня есть сомнения, что написание безопасного и корректного софта — простое и массовое занятие с какими угодно технологиями

                                                                                      У меня тоже. О чем тогда спор?


                                                                                      Просто мало кто знает.

                                                                                      Это и называется опыт.


                                                                                      Ну и если эта самая внимательность у вас бесконечная.

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

                                                                                        +4
                                                                                        Я о том как писать код собственно.

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


                                                                                        У меня тоже. О чем тогда спор?

                                                                                        О том, что с некоторыми инструментами это всё же делать проще, чем с другими.


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


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


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

                                                                                        Очень плохо, что никого не волнует.


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

                                                                                          –1
                                                                                          О том, что с некоторыми инструментами это всё же делать проще, чем с другими

                                                                                          Да, проще. Может быть. Чуть-чуть. Но не на порядок. Правильные "паттерны" (паттерны не в том смысле, как подумали) дают выигрыш сильно больше.


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

                                                                                          Да хоть комментарий можно добавить, я не знаю

                                                                                            +3
                                                                                            Да, проще. Может быть. Чуть-чуть. Но не на порядок. Правильные "паттерны" (паттерны не в том смысле, как подумали) дают выигрыш сильно больше.

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


                                                                                            Да хоть комментарий можно добавить, я не знаю

                                                                                            Вот только если бы эти комментарии машина бы как-то проверяла и сама ругалась, если они либо устаревают, либо вы им не следуете…

                                                                                              0
                                                                                              Вот только если бы эти комментарии машина бы как-то проверяла и сама ругалась, если они либо устаревают, либо вы им не следуете…

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

                                                                                                +3
                                                                                                Вы столкнулись с «парадоксом Блаба».
                                                                                                for(var i = 0; i < a.length; i++) {
                                                                                                  f(a, i);
                                                                                                }
                                                                                                
                                                                                                function f(a, i) {
                                                                                                  a[i] = 0;
                                                                                                }
                                                                                                Без зависимых типов, нам нужно либо в теле функции f проверять выход за пределы массива руками, либо во время выполнения будет ошибка, если кто-то передаст неверный индекс. Зависимые типы позволяют убрать проверки, доказав, что индекс находится в нужных пределах.
                                                                                                  0
                                                                                                  Но в данном примере что даст проверка на верный индекс при вызове функции? Проверим что индекс верный на входе в функцию. А в теле у нас такой код будет

                                                                                                  function f(a, i) {
                                                                                                    a[i+1] = 0;
                                                                                                  }
                                                                                                  

                                                                                                  И второе, нам же надо либо верифицировать всю программу и тогда она будет написана примерно никогда, либо верифицировать только критически важные моменты, но так как это человек решает что критически важно а что нет, то он снова ошибается. А даже если не ошибается и верно опеределил все критически важные моменты, то 90% работы он уже сделал, надо более внимательно их обработать. Разве нет?
                                                                                                    0
                                                                                                    А в теле у нас такой код будет

                                                                                                    Отличный пример! Тогда оператор [] потребует от вас доказательство, что i + 1 корректен, а у вас его нет, и компилятор даст вам по рукам.


                                                                                                    И второе, нам же надо либо верифицировать всю программу и тогда она будет написана примерно никогда

                                                                                                    Не то что никогда, но медленно, да. Медленнее, чем если ничего не верифицировать и фигачить в продакшен как есть.

                                                                                                      +1
                                                                                                      Но в данном примере что даст проверка на верный индекс при вызове функции?
                                                                                                      Нет. Проверяется индексация и она поднимается по стеку вызова. Каждое действие с i всего лишь смещает диапазон.

                                                                                                      То есть в моём примере это -1 < i < a.length, в вашем -2 < i < a.length - 1
                                                                                                      И второе, нам же надо либо верифицировать всю программу и тогда она будет написана примерно никогда, либо верифицировать только критически важные моменты
                                                                                                      В идеале, абсолютно внимательный программист на си и так не забывает ставить эти проверки. Проверять придётся далеко не каждую строку, например, мой пример выше соберётся без изменений, просто потому, что компилятор может доказать его самостоятельно. Если же речь идёт об уже написанном коде, то никто не мешает внедрять проверку постепенно, шаг за шагом.
                                                                                                      –1

                                                                                                      А можно так
                                                                                                      for(var i = 0; i < a.length; i++) {
                                                                                                      f(a[i], i);
                                                                                                      }
                                                                                                      Тогда никакие проверки в f не нужны вообще

                                                                                                        +1
                                                                                                        Ваш пример полагает, что функция f приватна, и никак, кроме как из for её не вызвать. Для библиотечных функций такой подход не годится, да и для крупного проекта тоже.
                                                                                                          0

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

                                                                                                            0
                                                                                                            Зависимые типы гарантируют, что как код внутри f, так и вызывающий f не будут приводить к выходу за границы массива.
                                                                                                              –2

                                                                                                              В примере выше я обошолся без завтипов. Несите другой

                                                                                                                +1
                                                                                                                Вы вынесли индексацию за пределы функции. Если у вас структура сложнее массива или обращение будет по десятку индексов, вы тоже будете выносить этот код выше?
                                                                                                                  0

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

                                                                                                                    0
                                                                                                                    Держите несколько примеров. Только не говорите про тесты, так как вам придётся тестировать вызывающий код, а он ещё не написан.
                                                                                                                      –1

                                                                                                                      Ну посмотрел, не нашел ничего, что нельзя было бы покрыть обычными типами с проверкой в конструкторе. Да и тесты никто не отменял, чтоже в них плохого?
                                                                                                                      А вот вы скажите мне одну вещь. В этой статье рассматривается пример деления на 0. И для этого вводится специальный тип, который заставляет проверять, является ли делитель нулем. Но (не знаю, так ли в haskell) исключительной также является ситуация деления maxint на -1. Где это учитывается? Автор об этом забыл?

                                                                                                                        +2
                                                                                                                        Ну посмотрел, не нашел ничего, что нельзя было бы покрыть обычными типами с проверкой в конструкторе.
                                                                                                                        Надёжность получается как у интерпретируемых языков — до тех пор, пока данная ветвь кода не выполниться, о существовании ошибки знать никто не будет.
                                                                                                                        Да и тесты никто не отменял, чтоже в них плохого?
                                                                                                                        Вы будете писать тесты каждому, кто скачает вашу библиотеку? Если вы пишите код в команде, то вы непрерывно отслеживаете кто и с какими аргументами вызывает ваши функции?
                                                                                                                        Но (не знаю, так ли в haskell) исключительной также является ситуация деления maxint на -1. Где это учитывается?
                                                                                                                        ghci
                                                                                                                        GHCi, version 8.6.5: http://www.haskell.org/ghc/ :? for help
                                                                                                                        Prelude> :t 5 / 7
                                                                                                                        5 / 7 :: Fractional a => a
                                                                                                                        Prelude> 5 / 7
                                                                                                                        0.7142857142857143
                                                                                                                        Prelude>
                                                                                                                          0

                                                                                                                          Я не понял, а если деление вещественное, разве на него действуют правила деления на 0?

                                                                                                                            0

                                                                                                                            На самом деле тут хаскелевская prelude в очередной раз страдает:


                                                                                                                            λ> (minBound :: Int) `div` (-1)   
                                                                                                                            *** Exception: arithmetic overflow
                                                                                                                            +1
                                                                                                                            Ну посмотрел, не нашел ничего, что нельзя было бы покрыть обычными типами с проверкой в конструкторе.

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


                                                                                                                            Да и тесты никто не отменял, чтоже в них плохого?

                                                                                                                            Что за их полнотой, корректностью и актуальностью никто не следит. И тесты не помогают писать код, а типы — помогают, вплоть до того, что всякие агды с идрисами умеют генерировать код по типам (пусть и не во всех случаях, но очень часто это полезно, и нередко написание кода превращается в поток команд case split — несколько proof search'ей на каждую ветку, повторить).

                                                                                                                              0
                                                                                                                              а попробуйте удалить доказательство в какой-нибудь функции посередине

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


                                                                                                                              Что за их полнотой, корректностью и актуальностью никто не следит

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

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

                                                                                                                                А для прототипирования есть специальные методы, которые позволяют обходить проверки, например unwrap для option, если что-то пойдёт не так, то программа безопасно упадёт.
                                                                                                                                  0

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


                                                                                                                                  И как потом убирать все unwrap из кода? Плюс хорошо, когда они есть.

                                                                                                                                    0
                                                                                                                                    И как потом убирать все unwrap из кода?
                                                                                                                                    Как обычно убирают костыли — переписать небольшие фрагменты. В случае rust, это будет либо match, либо if let. Возможно потребуется дописать какой-то код, вроде сообщения пользователю, что он сделал что-то не то, например ввёл строку вместо числа.
                                                                                                                                      0

                                                                                                                                      Это понятно. Я имею в виду как найти все такие места. И что делать с типами у которых unwrap метода нет

                                                                                                                                        +1

                                                                                                                                        Есть инструментарий, который помогает поймать все вызовы panic!. Все методы типа unwrap и прочие, которые просто прерываю выполнение программы в конечном итоге вызывают panic!.

                                                                                                                                          0

                                                                                                                                          Но смысл то не в том, чтобы дождаться паники, а в том, чтобы избавиться от всех unwrap

                                                                                                                                            0

                                                                                                                                            Неясно выразился, этот инструмент "ловит" во время компиляции. Превращает наличие вызова panic! в ошибку компиляции. Грубо говоря проверяет наличие "call stdlib_panic" в коде пользователя.

                                                                                                                                              0

                                                                                                                                              Ок, так понятнее

                                                                                                                                          0
                                                                                                                                          С помощью соответствующего анализатора. Вот конкретно для rust.

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

                                                                                                                                          Если взять другой язык, например Crystal, то там будет ещё проще.
                                                                                                                                          a = some_condition ? nil : 3
                                                                                                                                          if a.nil?
                                                                                                                                            # here a is Nil
                                                                                                                                          else
                                                                                                                                            # here a is Int32
                                                                                                                                          end
                                                                                                                                            0

                                                                                                                                            Анализаторы и в c++ можно использовать

                                                                                                                                              0
                                                                                                                                              Мне кажется, или мы идём на второй круг? В случае rust или какого-то другого безопасного языка анализатор нужен только для того, чтобы убедиться, что код не падает, так как кто-то забыл проверить какое-то условие. В случае плюсов анализатор нужен для уменьшения количества очевидной порчи памяти, не очевидную порчу он не распознает.
                                                                                                                                                –2

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

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

                                                                                                                                    Вы так всё время говорите классы, как будто хотя бы есть языки одновременно с завтипами и классами :]


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

                                                                                                                                    Но его нужно сильно больше, потому что машина вам не помогает, в отличие от типов.

                                                                                                                                0

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

                                                                                                                                  0
                                                                                                                                  Я бы решил его, обмазавшись проверками по максимому. Проверка, что результирующая строка не пустая, проверка, что файл открылся, еще к-нибудь. Как решть эоо завтипами?
                                                                                                                                  Требуется доказательство наличия третьего элемента в массиве, а поскольку доказательства нет, то код не пройдёт проверку.

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

                                                                                                                                    Ну и как бы это выглядело хотябы в псевдокоде?

                                                                                                                                      0
                                                                                                                                      Ориентировочно как-то так
                                                                                                                                      {-@ type PathList = {p : List | length p > 2} @-}
                                                                                                                                      Так мы объявляем тип для parts. Потом, после этого явно определяем тип выражения
                                                                                                                                      getUserName :: Home -> Text
                                                                                                                                      getUserName homeDir = parts !! 2
                                                                                                                                        where
                                                                                                                                          parts :: [Text] -> PathList
                                                                                                                                          parts = splitOn "/" homeDir
                                                                                                                                      После этого, можно запускать проверку типов.
                                                                                                                                        0

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

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

                                                                                                                                            Ну вот я не понимаю. Вот был код под линукс, который както вызывал эту функцию. Все работает, не падает. Потом переносим этот код под винду. Почему он вдруг упадет в compile time?

                                                                                                                    +1

                                                                                                                    Хорошо. Пример посложнее. Вот есть такая функция:


                                                                                                                    decodeACs :: HuffmanDecoder -> BitVec -> Word16 -> Maybe (V.Vector Float, BitVec)
                                                                                                                    decodeACs hm bv₀ initial = runST $ runMaybeT $ do
                                                                                                                      mvec <- VM.replicate 64 0
                                                                                                                      VM.unsafeWrite mvec 0 $ fromIntegral initial
                                                                                                                      let go cnt bv | cnt >= 64 = pure bv
                                                                                                                                    | otherwise = do
                                                                                                                            (rawCode, bv') <- first fromIntegral <$> lookup hm bv
                                                                                                                            case rawCode of
                                                                                                                                 0    -> pure bv'
                                                                                                                                 0xf0 -> go (cnt + 16) bv'
                                                                                                                                 _    -> do let (skip, code) = (rawCode .>>. 4, rawCode .&. 0x0f)
                                                                                                                                                (bits, bv'') = (takeWord16Bounded code bv', dropBits code bv')
                                                                                                                                                value = decodeWord8 code bits
                                                                                                                                            VM.unsafeWrite mvec (cnt + skip) $ fromIntegral value
                                                                                                                                            go (cnt + skip + 1) bv''
                                                                                                                      bv <- go 1 bv₀
                                                                                                                      (\vec -> (unZigzagify vec, bv)) <$> V.unsafeFreeze mvec

                                                                                                                    VM.replicate 64 0 создаёт массив длиной 64 элемента из одних нулей. Как здесь избежать unsafeWrite, имея гарантии от компилятора о безопасности записей?


                                                                                                                    К слову, если там заменить проверку cnt >= 64 на cnt == 64, то эта функция станет некорректной и начнёт падать (или зацикливаться) на некоторых невалидных данных.


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

                                                                                                                      0

                                                                                                                      К сожалению я не понял, что от меня требуется. Гарантировать безопасность cnt>=64? Если сильно упороться, то в oop языках можно создать целый класс cnt, который принимает по ссылке вектор и имеет метод с выразительным именем, который проверяет, что не превысили размер вектора

                                                                                                                        0

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

                                                                                                                          +1
                                                                                                                          Проверки

                                                                                                                          Имеют стоимость в рантайме. Это кусок jpeg-декодера, здесь лишний if на каждую операцию записи — это дорого.


                                                                                                                          тесты

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


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


                                                                                                                          что-то можно повыносить в классы

                                                                                                                          Классы — не панацея.

                                                                                                                            –1
                                                                                                                            Это кусок jpeg-декодера, здесь лишний if на каждую операцию записи — это дорого.

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

                                                                                                                    +2
                                                                                                                    Ну можно давать нормальные имена переменных

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


                                                                                                                    ставить проверки

                                                                                                                    Имеют рантайм-стоимость, компилятор не гарантирует их полноту.


                                                                                                                    писать тесты

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


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


                                                                                                                    Альтернатива — завтипы, но насколько они лучше всего перечисленного?

                                                                                                                    Ну, плюсы:


                                                                                                                    1. Имеют минимальную стоимость в рантайме. Coq вообще при компиляции экстракции вырезает то, что живёт в Prop (это все доказательства), идрис тоже очень агрессивно вырезает, плюс что в идрисе, что в агде есть аннотации для runtime irrelevance. В идрисе 2, кстати, это очень изящно совмещается с тамошней вариацией линейных типов — quantitative type theory, и можно, например, аннотировать классический тип вектора (ну вот это вот Vect n Int для ровно n интов) так, чтобы его длина не вылезала в рантайм.
                                                                                                                    2. Компилятор проверяет полноту настолько, насколько это можно. У вас просто не получится написать std::optional<T>::operator* ∀ a. Maybe a → a. Эта функция не может быть тотальной.
                                                                                                                    3. Компилятор проверяет корректность.

                                                                                                                    Минусы:


                                                                                                                    1. Надо думать о доказательствах.

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

                                                                                                                      0

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

                                                                                                                        0

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

                                                                                                                          0

                                                                                                                          Может потому, что их там не используют? Как и завтипы впрочем

                                                                                                                            0

                                                                                                                            Тесты там есть, документация есть, даже имена переменных осмысленные. Вроде как используют.

                                                                                                                              0

                                                                                                                              А о каком проекте вообще речь?

                                                                                                                                0

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

                                                                                                                    0

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

                                                                                                                      +1

                                                                                                                      Конечно.


                                                                                                                      data Timezone = ...
                                                                                                                      
                                                                                                                      data DateTime : Timezone -> Type where
                                                                                                                        ...
                                                                                                                      
                                                                                                                      timespan : DateTime tz -> DateTime tz -> ...

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


                                                                                                                      parseWithoutTZ : String -> Either ParseError (DateTime tz)
                                                                                                                      parseWithTZ : String -> Either ParseError (tz ** DateTime tz)

                                                                                                                      В первом случае таймзона определяется вызывающим кодом (например, при парсинге строк, где формат не подразумевает указание таймзоны), во втором — она определяется внутри функции-парсера (и возвращается зависимая пара). Во втором же случае код типа


                                                                                                                      (tz1 ** dt1) <- parseWithTZ str1
                                                                                                                      (tz2 ** dt2) <- parseWithTZ str2
                                                                                                                      let span = timespan dt1 dt2

                                                                                                                      не соберётся, так как вы не проверили равенство tz1 и tz2. Соответственно, надо вместо этого что-то вроде


                                                                                                                      (tz1 ** dt1) <- parseWithTZ str1
                                                                                                                      (tz2 ** dt2) <- parseWithTZ str2
                                                                                                                      case decEq tz1 tz2 of
                                                                                                                           Yes Refl -> timespan dt1 dt2 ...
                                                                                                                           No _ -> Left "разные таймзоны :("
                                                                                                                        0

                                                                                                                        Не, речь не о присутствии/отсутствии таймзоны.
                                                                                                                        Вопрос в том, чтобы не путать между собой даты в разных таймзонах.

                                                                                                                          +2

                                                                                                                          Мой комментарий именно про это. timespan с типом как выше не позволит вам путать даты в разных таймзонах, потому что даты параметризованы таймзонами, и свободная переменная tz, обозначающая некую любую таймзону, общая между двумя аргументами timespan. Поэтому вы должны доказать тем или иным образом, что они на самом деле равны — либо статически (если у вас в коде написано, условно, let someDT = MkDT 2021 4 26 16 53 36 CDT и аналогично для другой даты), либо выполнив рантайм-проверку (как в коде с decEq), если вы таймзоны читаете из файла/сети.


                                                                                                                          Отличный пример, на самом деле.

                                                                                                                            0

                                                                                                                            Я правильно понимаю, что выражение
                                                                                                                            parseWithTZ : String -> Either ParseError (tz ** DateTime tz)
                                                                                                                            надо читать как


                                                                                                                            сигнатура функции, которая
                                                                                                                            1. называется parseWithTZ
                                                                                                                            2. принимает String
                                                                                                                            3. возвращает Either, который содержит сообщение об ошибке парсинга или кортеж значений
                                                                                                                              кортеж содержит дату и тип

                                                                                                                            ?


                                                                                                                            в условной Jawa подобная функция бы выкидывала условный FormatException и возвращала бы условный DateTimeWithTimeZone


                                                                                                                            Как тогда получается написать
                                                                                                                            case decEq tz1 tz2 of
                                                                                                                            если tz1 потенциально может вообще не быть?


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

                                                                                                                              +1
                                                                                                                              Я правильно понимаю, что [...]

                                                                                                                              Почти. С одним мелким, но очень важным уточнением:


                                                                                                                              кортеж значений кортеж содержит дату и тип

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


                                                                                                                              Как тогда получается написать [...] если tz1 потенциально может вообще не быть?

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


                                                                                                                              Можете представлять себе это как цепочку


                                                                                                                              case parseWithTZ str1 of
                                                                                                                                   Left err -> pure err
                                                                                                                                   Right (tz1 ** dt1) ->
                                                                                                                                       case parseWithTZ str2 of
                                                                                                                                            Left err -> pure err
                                                                                                                                            Right (tz2 ** dt2) -> ... тут код дальше ...

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

                                                                                                                              Он там неявно скрыт за стрелочкой <-, тем, во что она дешугарится в do-нотации, и инстансом Monad для Either err. Но это не имеет прямого отношения к обсуждаемой теме — можно представить себе, что эти функции всегда завершаются успешно, что никаких Either там нет, и сфокусироваться непосредственно на части с decEq. Там самое интересное.

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

                                                                                                                        Разве для этого нужны зависимые типы? Если у нас DateTime параметризируется тайм зоной, то этого достаточно?

                                                                                                                          +2

                                                                                                                          Завтипы тут будут нужны для того, чтобы иметь возможность прочитать таймзону из файла и всё равно статически проверять корректность операций с датами. В C++ (или в хаскеле до гипотетического -XDependentHaskell) вы можете так параметризовать, но конкретные значения таймзон у конкретной даты тогда должны быть известны статически.

                                                                                                                    +5
                                                                                                                    Ну расскажу я, как сделать формально верифицированное красно-чёрное дерево или сортировку, неужто это будет полезно в контексте этой дискуссии?

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

                                                                                                      –2
                                                                                                      И что новое вы создаёте ножом и вилкой?


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

                                                                                                      Так устроит?

                                                                                                      Более конкретно — есть пациент, есть хирург. У хирурга в руках скальпель. При неосторожном движении пациент может скончаться прямо на столе. Если все сделать правильно — пациент будет жив и здоров.

                                                                                                      Где же эти разработчики с высокой квалификацией, над чем они работают?


                                                                                                      Не знаю. Видимо, это те, кто создавали такие вещи как AS/400 (написана на плюсах), AIX, QNX (согласитесь — это достаточно серьезные коммерческие продукты).

                                                                                                      Точно не над openssl, точно не над ядром линукса


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

                                                                                                      Ну бывает, ну шутя
                                                                                                      Сбилась с верного путя
                                                                                                      Так ведь я — дитя природы,
                                                                                                      Пусть дурное, но дитя
                                                                                                      (с) Л.Филатов


                                                                                                      Не люблю такую аргументацию, но позволю себе. Так вот, примерно за 17 лет писания кода на плюсах, из которых лет 15 я получал за это деньги, из которых лет 10 — фуллтайм, я убедился, что на плюсах (и на С) писать безопасный и надёжный код в программах сложнее хелловорлда за адекватное время невозможно.


                                                                                                      Странно, но у меня почему-то получалось и получается.
                                                                                                      На С начал писать в 88-м (Turbo C 1.0). С 91-го — фуллтайм за деньги. Когда перешел на плюсы не вспомню уже, помню что это был (что удалось достать) Turbo C++ 2.0
                                                                                                      С 17-го C/C++ скорее вспомогательный язык т.к. основной — RPG (на платформе IBM i). Но часть вещей, требующих более тесного взаимодействия с системой, пишутся на С/С++.
                                                                                                      Просто нужно понимать где и чем хорош С, и где он плох.

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


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

                                                                                                      Дай ребенку скальпель порежется. Дай скальпель хирургу — удалит опухоль.
                                                                                                        +4
                                                                                                        Пусть будет не нож.

                                                                                                        Забавно, как ваша изначальная аналогия поменялась.


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

                                                                                                        И вы будете выступать против запретов агитации использования более безопасного оборудования взамен менее безопасного? Therac-25 зря чинили, надо было просто обязать оператора печатать на клавиатуре медленно? К слову, да, там так и сделали, но требованием печатать медленно не ограничились.


                                                                                                        Или вот я покупаю машину со специальными фичами безопасности — blind spot monitor, brake assist, радар с автообнаружением препятствий — это я тоже дурак и использую технику для дураков?


                                                                                                        Видимо, это те, кто создавали такие вещи как AS/400 (написана на плюсах)

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


                                                                                                        AIX

                                                                                                        тыц. 335 дырок, 105 из них — наш любимый buffer overflow. Не, не умеют авторы AIX в плюсы.


                                                                                                        QNX

                                                                                                        тыц


                                                                                                        Multiple buffer overflows… stack-based buffer overflow… race condition… race condition… Не, не умеют авторы QNX в плюсы. Ну и такое чувство, что после 2006-го года она резко стала неинтересна.


                                                                                                        Это бесплатные продукты. Они создаются в свободное от зарабатывания денег время энтузиастами.

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


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

                                                                                                        Какие-то странные специалисты, которые могут делать свою работу хорошо, только если они несут ответственность. Кстати, какую ответственность несут конкретные разработчики QNX или AIX?


                                                                                                        Странно, но у меня почему-то получалось и получается.

                                                                                                        Возможно, вы сверхчеловек.


                                                                                                        Впрочем, я первые лет 10 тоже думал, что у меня получается. А потом понял, что очень сильно ошибался.


                                                                                                        Просто нужно понимать где и чем хорош С, и где он плох.

                                                                                                        C хорош только для взаимодействия с ОС, предоставляющей C API. Для разработки требовательного к производительности ПО он плох (компилятор видит не так много, успехов с инлайнингом компаратора для qsort, например), для разработки надёжного ПО он очень плох.


                                                                                                        Ну если для Вас в С++ главное шаблоны…

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


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

                                                                                                          –3
                                                                                                          это я тоже дурак и использую технику для дураков?

                                                                                                          Ну учитывая недавний случай, когда двое граждан как-то умудрились обмануть так называемый «автопилот» теслы и заставить его ехать, когда на сиденье водителя вообще никого не было… поневоле задумаешься на тему того, на какую аудиторию это все рассчитано. Перефразируя известное высказывание Мольтке про приказы, «любое устройство, которое может быть использовано неправильно, обязательно будет использовано неправильно».
                                                                                                            –1
                                                                                                            blind spot monitor, brake assist, радар с автообнаружением препятствий

                                                                                                            Это всё примерно -Wall. А автопилот автомобильный — то вообще рандом, никакими доказательствами и детерминизмом там не пахнет.

                                                                                                            0
                                                                                                            Видимо, это те, кто создавали такие вещи как AS/400 (написана на плюсах), AIX, QNX (согласитесь — это достаточно серьезные коммерческие продукты).
                                                                                                            image

                                                                                                            Сколько там уязвимостей в ms-dos? Всем срочно переходить на ассемблер.
                                                                                                              0
                                                                                                              ms-dos написана на ассемблере
                                                                                                                0
                                                                                                                Полагаю, что в сумме, в ms-dos всё же меньше уязвимостей, чем в linux.