Комментарии 38
а на другой C++, где свобода обращения с памятью может обернуться утечками или, что еще хуже, непредсказуемым UB (англ. undefined behavior, в
ряде источников непредсказуемоенеопределенное поведение).
Утечки памяти в Rust очень даже возможны, наверно поэтому они считаются не ошибками, а фичами, наличие которых не влияет на безопасность управления памятью :-)
Тогда как UB в С++ (англ. undefined behavior) именно неопределенное поведение, т.е. это не зафиксировано в стандарте С++ и конкретная реализация оставлена на усмотрение разработчиков компилятора. Это может быть "Abnormal program termination", так же как в Rust, это может быть выбрасывания исключения, которое можно поймать и обработать в коде программы, а может быть и взрыв системного блока (и все почему-то считают, что UB в С++ это всегда последний вариант).
UB означает, что может произойти все что угодно! Это не поведение, которое зависит от железа или компилятора! UB — это буквально недостижимый код с точки зрения компилятора, то, чего не может случиться никогда, и на что компилятор рассчитывает, применяя оптимизации. Потому что если это случилось — значит код, который был скомпилирован, буквально НЕ является программой на С/С++.
Недостижимый код не имеет никакого отношения к UB, которое вполне себе может случиться и иногда случается.
Скомпилированная программа на С/С++ является С/С++ программой по определению. Однако из-за наличия UB она может не соответствовать стандарту С++, но от этого она программой на С/С++ быть не перестанет.
Ну вот это огромное заблуждение. Я не говорю, что UB — это недостижимый код! Я говорю о том, что компилятор может и будет оптимизировать полагаясь на то, что UB никогда не происходит. Например, удаляя "лишние" с его точки зрения бранчи, в которых есть UB.
То, что оно случается, это правда. Только не иногда, а практически в любой достаточно большой кодовой базе.
Я не говорю, что UB — это недостижимый код!
UB — это буквально недостижимый код с точки зрения компилятора ...
Циклические ссылки в любом языке могу привести к утечкам памяти. Даже GC не всегда может их заметить. Почему вы считаете это проблемой только Rust?
поэтому они считаются не ошибками, а фичами
Обоснуйте. Пока что это просто ваша фантазия, которую вы приписываете разработчикам языка Rust.
Тогда как UB в С++ (англ. undefined behavior) именно неопределенное поведение, т.е. это не зафиксировано в стандарте С++ и конкретная реализация оставлена на усмотрение разработчиков компилятора.
Вы категорически не правы (я очень надеюсь что просто заблуждаетесь). Undefined behavior не равно unspecified behavior, и в стандарте напрямую написано "код, содержащий UB, не является кодом на С++". В отличие от unspecified behavior.
Циклические ссылки в любом языке могу привести к утечкам памяти. Даже GC не всегда может их заметить. Почему вы считаете это проблемой только Rust?
Обоснуйте. Пока что это просто ваша фантазия, которую вы приписываете разработчикам языка Rust.
Я не считаю циклические ссылки проблемой только Rust. Я писал, и сразу привел ссылку на документацию, в которой разработчики Rust утечки памяти объявляются "безопасными":
Rust’s memory safety guarantees make it difficult, but not impossible, to accidentally create memory that is never cleaned up (known as a memory leak). Preventing memory leaks entirely is not one of Rust’s guarantees, meaning memory leaks are memory safe in Rust.
Rust’s memory safety guarantees make it difficult, but not impossible, to accidentally create memory that is never cleaned up (known as a memory leak). Preventing memory leaks entirely is not one of Rust’s guarantees, meaning memory leaks are memory safe in Rust.
Так тут и написано, что утечку памяти невозможно выявить на этапе компиляции. По этому Rust не дает этих гарантий. И другие языки не дают.
По-этому вы не смогли обосновать что это "фича", ведь другие языки имеют точно такую же "фичу".
Другие языки не называют утечки памяти "безопасными"
Она safe в смысле что не вызывает UB, и может произойти без блока unsafe. Это просто описание фактического положения дел.
Другие языки не называют утечки памяти "безопасными"
Но и не называют их "небезопасными". Вот если в другом языке будет написано что утечка памяти unsafe или unsound, тогда будет о чем говорить. Можете привести такой пример?
Если честно, то я уже запутался в ваших отрицаниях отрицаний при попытке доказать, что утечки памяти в Rust являются самими "безопасными", чем любом другом языке программирования. Давайте я вам немного с этим помогу :-)

Если честно, то это вы придираетесь к словам, а не к сути.
Я понимаю вашу претензию к автору статьи, он не прав про утечки памяти в С++. Но и вы не правы, они абсолютно одинаковые в С++ и Rust. И способы их избежать одинаковые: слабые ссылки.
Но и вы не правы, они абсолютно одинаковые в С++ и Rust. И способы их избежать одинаковые: слабые ссылки.
Так это я утверждаю, что утечка памяти, это всегда ошибка не зависимо от языка программирования. И моя претензия не к автору статьи, а к разработчикам Rust, которые в своем желании рекламироваться как "безопасный язык для управления памятью", решили разделить утечки памяти на "правильные" и "не правильные", а автор статьи это заблуждение просто повторил.
Прочитайте наконец обсуждаемую ссылку дальше заголовка. Там написано, что утечки памяти это плохо, и как их предотвратить:
Compared to a real-world program, the consequences of creating a reference cycle in this example aren’t very dire: right after we create the reference cycle, the program ends. However, if a more complex program allocated lots of memory in a cycle and held onto it for a long time, the program would use more memory than it needed and might overwhelm the system, causing it to run out of available memory.
Creating a reference cycle would be a logic bug in your program that you should use automated tests, code reviews, and other software development practices to minimize.
Вы выдираете слова из контекста, и этим гордитесь почему-то.
Ага, "пишите и тестируйте программы правильно и не пишите неправильно". Но чем этот совет и способ предотвращения ошибок в программах не подходит, например, для С++?
Конечно. Вся статья о том, как "писать правильно и не писать неправильно". Что же в этом плохого, раз компилятор не может этот момент проверить за программиста?
Но чем этот совет и способ предотвращения ошибок в программах не подходит, например, для С++?
Подходит, о чем я и писал на 4 комментария выше.
и в стандарте напрямую написано "код, содержащий UB, не является кодом на С++"
По вашему выходит, что как только мы пытаемся разименовать нулевой указатель, например, у std::shared_ptr, Dereferences the stored pointer. The behavior is undefined if the stored pointer is null., то программа перестает быть программой на С++? Или она изначально таковой не являлась? Или превращается в корректную программу только после сохранения в std::shared_ptr корректного указателя?
Тут надо рассматривать 3 случая:
1) На этапе компиляции известно что null, и мы его разименовываем. Тогда изначально не являлась программой на С++.
2) На этапе компиляции известно что не null, и мы его разименовываем. Тогда UB нет.
3) На этапе компиляции не известно что лежит null / не null, и мы его разъименовываем без проверки. Тогда программа потенциально содержит UB, а фактически содержит UB в момент фактического разъименования null (и в этот момент перестает быть программой на С++).
Но изначальный мой посыл был что Undefined behavior гораздо опаснее unspecified behavior, а вы пытаетесь их приравнять.
Но изначальный мой посыл был что Undefined behavior гораздо опаснее unspecified behavior, а вы пытаетесь их приравнять.
Нет, мой посыл был, что Undefined behavior, это именно поведение не определенно и согласно стандарту не рассматривается. Хотя согласен, что мой комментарий наверно можно трактовать как unspecified behavior согласно стандарту, но имелось ввиду буквальное "поведение не определено", а не "unspecified behavior".
А что касается вашей трактовки программы на С++ или не программы на С++, то я с вами вообще не согласен. Программа, это результат работы компилятора. Она материальный объект и не может находится в неопределенном состоянии из-за своего поведения как "С++ программа Шредингера", тогда как проверка программы на соответствие стандарту С++ вполне укладывается в эту логику.
Ассемблерные инструкции в скомпилированной программе конечно определены. Но одна из проблем UB в том, что при перекомпиляции они могут быть уже другими. И даже без изменения исходного кода. А при полной перекомпиляции может быть 3-й вариант.
Не говоря уже о разных версиях компилятора, целевого процессора, ОС компилятора и целевой ОС и т.д.
не может находится в неопределенном состоянии
Вообще-то может. Приведу простой пример:
Процессор ARM может (и постоянно делает) переупорядочивать на лету ассемблерные инструкции. И при многопоточном доступе может возникнуть data race, если не выставлены правильные барьеры памяти.
В результате один и тот же код (одинаковые инструкции ассемблера) на одном процессоре с одинаковыми входными данными выдаст разный результат. Скомпилированная программа находится в неопределенном состоянии, и это тоже UB.
Тогда вам следует дать определение, что такое "программа", в противном случае появляется очень возможностей для манипуляций словами и подмен понятиями.
Так например, компиляция одного и того же кода всегда дают разные двоичные файлы программы, если не используются специальные методики для двоичной воспроизводимости сборки, а транслирование инструкций во внутренний формат происходит и для других типов процессоров, а не только ARM.
Для кода без UB (где проверками обеспечивается отсутствие UB при любых входных данных) компилятор может создать разные наборы ассемблерных инструкций, но они выдадут одинаковый результат при одинаковых входных данных.
А вот код с UB (который при некоторых входных данных вызывает UB) может выдать разный результат при каждом запуске. Я выше вам привел пример с ARM.
Или например, выход за границы массива позволяет читать "соседнюю" память, и при каждом запуске этого кода (даже в пределах одного запуска программы) может быть разный результат, т.к. другие части программы поменяли эту "соседнюю" память.
Многократно эксплуатируя эту уязвимости, можно прочитать ключ шифрования например. А вы говорите:
Программа, это результат работы компилятора. Она материальный объект и не может находится в неопределенном состоянии
Не ужели вы не видите тут ошибку в своей логике??
Да бог с ним с UB, лучше скажите, что такое программа на С++?
Вот только пожалуйста не говорите, что программа на С++, это как программа на С++, только без UB :-)
Но везде имел ввиду "код на С++".
Хорошо, пусть так, так что же такое "код на С++" ?
Тогда я перефразирую тот комментарий:
Тут надо рассматривать 3 случая:
1) На этапе компиляции известно что есть UB (например выход за пределы массива с известным индексом). Тогда код изначально не является кодом на С++.
2) На этапе компиляции известно что нет UB (все потенциальные места обвешаны проверками). Тогда код является кодом на С++.
3) На этапе компиляции нельзя доказать что нет UB, но есть места, потенциально содержащие UB (в них недостаточно проверок). Тогда нельзя сказать заранее, является ли кодом на С++.
Если при запуске такого кода происходит UB (например входные данные, которые не функция не ожидает), то в этот момент он перестает быть кодом на С++.
Вы не ответили на вопрос, что такое "код на С++", поэтому мы опять вынуждены вернуться к определению (Самурай без меча подобен самураю с мечом, но только без меча код на С++, это как код на С++, только без UB) :-)
Я считаю что ответил. И я правда не понимаю что вы ещё от меня хотите.
Напишите свою версию определения, может понятнее будет.
Да бог с ним с UB
Это вы так признали свою неправоту?)
так блок unsafe безопасен?
В одном моем проекте я использую крипто-алгоритм srp6 - моя адаптация, заточенная под архитектуру проекта. И вот при одновременном запуске множества инстансов возникла проблема в вычислении хэша. Пришлось адаптировать еще и с++ версию библиотеки и смотреть, в чем же разница (было давно, но там проблема была в какой-то малозаметной мелочи) - в итоге пофиксил. Ну и вот так тестировал: https://github.com/sergio-ivanuzzo/reproduce-cpp-to-rust
Увидел srp6 и сразу вспомнился wow :). Давно хотел написать минимально рабочий сервер для него, может заодно с ecs побаловаться. Если не затруднит, не могли бы вы посоветовать полезные ресурсы?
в моей организации https://github.com/tench-rt и клиент, и сервер, оба на Rust написаны. Поэтому можно наглядно посмотреть с обоих сторон, как это работает. Сервер именно простой, не игровой - во всяком случае, пока что не планировал его таким делать. Подойдут вам такие полезные ресурсы ?
Для ffi с C++ лучше использовать cxx, потому что умеет конвертировать плюсовые исключения в растовый Result. В случае с bindgen, исключение, которые уходит в Раст, - уб. Он больше для связки с Си подходит
FFI: как создать мост между Rust и C/C++