Любая проблема в unsafe коде раста (а если даже вы лично его не пишете, то в используемых вами разнообразных библиотеках его точно полно, как и в FFI-обертках к внешним библиотекам на других языках) может протечь куда угодно
То что UB протекает куда угодно - это свойство самого UB, и вызовет точно такие же проблемы в С, С++ и даже в языках со сборщиком мусора, при вызове FFI например.
Весь вопрос в разделении ответственности между авторами библиотек, и теми кто их использует.
Код библиотек c unsafe в любом случае нужно обвешивать тестими, проверять фаззинг, применять санитайзеры и статические анализаторы и т.д. Что в Rust, что в С, С++, прочих языках (при вызове FFI например).
А в прикладном коде мейнтейнер один раз напишет `#![forbid(unsafe_code)]` в main.rs, выставит сам список зависимостей в Cargo.toml, и может быть уверен что никакой джун не напишет прикладной код с UB.
А вы хотите чтобы все варианты UB были описаны на паре страниц текста? Ну поищите такой-же список хотя бы для С например..
Фраза The following list is not exhaustive; it may grow or shrink означает, что никто не будет вести полный (огромный) список UB, по крайней мере пока не будет формальной модели. Кроме того этот список будет постоянно меняться.
Прямо в 3-м абзаце идет ссылка на Rustonomicon, в котором каждый случай расписан подробно. В части языка там описаны безопасные операции, все остальные - потенциальное UB. А в стандартной библиотеке полностью описаны все случаи UB для каждой unsafe функции или типа.
Таким образом ваше утверждение полностью неверно. Писать unsafe код можно, и он будет гарантированно работать как задумывалось.
Для этого нужно: - не нарушать правила языка, описанные в Rustonomicon. Причем смотреть версию Rustonomicon, соответствующую Edition вашего кода (2015, 2018, 2021, 2024). Довольно похоже на версии С++, не находите? - не нарушать правила вызова unsafe функций из std и библиотек, см. документацию этих функций.
Файл /etc/resolv.conf это симлинк, и конечный файл автоматически заполняется через systemd-resolved. Вероятно он из затирает ваши изменения после перезагрузки.
Вам нужно настроить либо systemd-resolved, либо networkmanager, чтобы они генерировали /etc/resolv.conf с нужными вам настройками сети. Сам по себе rustup тут не при чем.
В алгоритмах не нужен. А вот в коде, который использует алгоритмы, borrow checker даёт существенные гарантии. Ничего похожего в С и С++ и близко нету.
Принцип такой: алгоритмы пишутся через unsafe опытными программистами, проверяются тестами, санитайзерами, фаззингом и т.д.
А обычный код без unsafe может писать любой джун, и у него никогда не вылезет UB, use after free, double free, разъименование null и т.д. Можно в main.rs еще добавить #[forbid(unsafe_code)] для гарантии.
Кстати, алгоритм у вас неправильный - tail никогда не обновляется ;) И, думаю, компилятор к этому никаких претензий не имеет ;)
Код напрямую перенесен из С в Rust, и я специально использовал только указатели и Box, чтобы можно было легко сравнить с кодом на С.
Этих двух методов достаточно, чтобы понять главную идею: Если алгоритм написан правильно, то абсолютно без разницы, сколько внутри unsafe. В С и С++ точно так же, только ключевого слова unsafe нету.
Современный С++ закрыл очень маленькую часть проблем, по сравнению с legacy С++. По субъективным ощущениям, 5% примерно от общего количества. В основном это наследие С (например это).
Но добавил новых проблем не меньше, раз, два, три, и т.д...
Перечитал тред выше. Я с вами согласен в части: - без unsafe написать двусвязный список можно, но будут дополнительные runtime проверки из-за реализации через слабые ссылки.
Но что мешает написать (или взять готовую из std) оптимальную реализацию через unsafe?
Без runtime проверок? Отрицаю.
Вы возможно не поняли мой вопрос. Допустим есть оптимальная реализация (как на С) через unsafe без дополнительных runtime проверок. Вы отрицаете что у нее можно сделать безопасное API?
In this work, we propose Stacked Borrows, an operational semantics for memory accesses in Rust. Stacked Borrows defines an aliasing discipline and declares programs violating it to have undefined behavior, meaning the compiler does not have to consider such programs when performing optimizations.
А под ней:
We also implemented this operational model in an interpreter for Rust and ran large parts of the Rust standard library test suite in the interpreter to validate that the model permits enough real-world unsafe Rust code.
А в самой статье описана реализация интерпретатора Miri, который проверяет код std на соответствие модели Stacked Borrows:
We implemented Stacked Borrows in Miri to be able to test existing bodies of unsafe Rust code and make sure the model we propose is not completely in contradiction with how real Rust code gets written. Moreover, this also served to test a large body of safe code (including code that relies on non-lexical lifetimes), empirically verifying that Stacked Borrows is indeed a dynamic version of the borrow checker and accepts strictly more code than its static counterpart.
Так что либо вы сами издеваетесь, либо мы по разному понимаем термин Aliasing Model.
Конечно. Например я обратил внимание, что вы даже не перешли по ссылке оппонента. В ней 11-я статья от 2020 года описывает то, что вам нужно. А вы привели две статьи 2018 года, конечно в них всё было в зачаточном состоянии.
Это не правда. Вот этот комментарий, и особенно комментарии по всей ветке ниже показывают, что ваша претензия к реализации связного списка не справедливы. На Rust можно написать связный список без unsafe, но нельзя написать оптимальную реализацию в стиле С.
А вот в этом комментарии и нижележащей ветке вам привели ссылки на доказательства корректности модели памяти Rust. Но не доказательства корректности её реализации в компиляторе.
Так и не лезут: компилятор допилили под требования Торвальдса, API выделения памяти допилили (чтобы возвращать OutOfMemory через Result, а также отделили библиотеку alloc от std). Вообще кучу всего в std доделали, и это будет полезно не только в Linux.
Сейчас Торвальдс требует опцию rustfmt, чтобы он не менял агрессивно форматирование блоков use. Звучит вполне логично, и это тоже сделают.
А Rust в ядре Linux есть и будет. И это мнение Торвальдса как раз основано на том, что его требования к языку и экосистеме учитывают и удовлетворяют тем или иным способом.
Работа с регистрами по определению unsafe, но в С и С++ нет такого ключевого слова, так что по кол-ву unsafe сравнивать некорректно.
Но поверх unsafe можно построить safe API, и этим повсеместно пользуются. Приведу простой пример:
Если взять стандартную библиотеку любого языка, то там 90% кода будет unsafe из-за системных вызовов, передачи указателей в ядро ОС, арифметики указателей ради быстродействия и т.д. Стандартной библиотекой пользуется любой программист на этом языке.
По моему опыту, скорость компиляции (инкрементальной) примерно одинаковая с С++. В работе с матрицами точно быстрее, где-то медленнее. В целом меня устраивает. В последней статье правильно указали про lto, в текущем проекте (верхний крейт 40kloc) инкрементальная компиляция 4-5 секунд, но если выключить lto то 1-1.5 секунды.
Не инкрементальную делаю наверное раз в пол года, когда обновляю компилятор, так что вообще не важно. И в последней статье как-раз про то, как её ускорить на билд сервере.
В BetterC, например. Это базовое требование для поддержки baremetal. Так что во всех ЯП с таким специалитетом.
Ну в С и С++ с этим как-то грустно, BetterC пока не пробовал.
Где можно взять компилятор Аргентум?
Код в статье выглядит интересно, но хотелось бы попробовать его для своих задач.
То что UB протекает куда угодно - это свойство самого UB, и вызовет точно такие же проблемы в С, С++ и даже в языках со сборщиком мусора, при вызове FFI например.
Весь вопрос в разделении ответственности между авторами библиотек, и теми кто их использует.
Код библиотек c unsafe в любом случае нужно обвешивать тестими, проверять фаззинг, применять санитайзеры и статические анализаторы и т.д. Что в Rust, что в С, С++, прочих языках (при вызове FFI например).
А в прикладном коде мейнтейнер один раз напишет `
#![forbid(unsafe_code)]` в main.rs, выставит сам список зависимостей в Cargo.toml, и может быть уверен что никакой джун не напишет прикладной код с UB.А вы хотите чтобы все варианты UB были описаны на паре страниц текста? Ну поищите такой-же список хотя бы для С например..
Фраза The following list is not exhaustive; it may grow or shrink означает, что никто не будет вести полный (огромный) список UB, по крайней мере пока не будет формальной модели. Кроме того этот список будет постоянно меняться.
Прямо в 3-м абзаце идет ссылка на Rustonomicon, в котором каждый случай расписан подробно. В части языка там описаны безопасные операции, все остальные - потенциальное UB. А в стандартной библиотеке полностью описаны все случаи UB для каждой unsafe функции или типа.
Таким образом ваше утверждение полностью неверно. Писать unsafe код можно, и он будет гарантированно работать как задумывалось.
Для этого нужно:
- не нарушать правила языка, описанные в Rustonomicon. Причем смотреть версию Rustonomicon, соответствующую Edition вашего кода (2015, 2018, 2021, 2024). Довольно похоже на версии С++, не находите?
- не нарушать правила вызова unsafe функций из std и библиотек, см. документацию этих функций.
Файл /etc/resolv.conf это симлинк, и конечный файл автоматически заполняется через systemd-resolved. Вероятно он из затирает ваши изменения после перезагрузки.
Вам нужно настроить либо systemd-resolved, либо networkmanager, чтобы они генерировали /etc/resolv.conf с нужными вам настройками сети. Сам по себе rustup тут не при чем.
Странно, у меня rustup на Raspberry Pi работает штатно. Вы его из пакета ставите, или с сайта?
В алгоритмах не нужен.
А вот в коде, который использует алгоритмы, borrow checker даёт существенные гарантии. Ничего похожего в С и С++ и близко нету.
Принцип такой: алгоритмы пишутся через unsafe опытными программистами, проверяются тестами, санитайзерами, фаззингом и т.д.
А обычный код без unsafe может писать любой джун, и у него никогда не вылезет UB, use after free, double free, разъименование null и т.д. Можно в main.rs еще добавить
#[forbid(unsafe_code)]для гарантии.Тут поспешил) Компилятор, кстати, выдает warning.
Вы игнорируете, что такой код уже существует в стандартной библиотеке. И в нем никаких дополнительных проверок по сравнению с С!
Но я понимаю, что в нем сложно разобраться, по-этому написал сильно упрощенную версию:
Код напрямую перенесен из С в Rust, и я специально использовал только указатели и Box, чтобы можно было легко сравнить с кодом на С.
Этих двух методов достаточно, чтобы понять главную идею:
Если алгоритм написан правильно, то абсолютно без разницы, сколько внутри unsafe. В С и С++ точно так же, только ключевого слова unsafe нету.
Пишем unsafe код в стиле С
Не оборачиваем его в runtime проверки, т.к. они не нужны
Выставляем наружу safe API
PROFIT!
Реализацию двусвязного списка я без проблем напишу через unsafe, и ассемблер будет одинаковый с таким же кодом на С. Никаких дополнительных проверок!
Вы долго еще будете изворачиваться и игнорировать ключевой вопрос:
Современный С++ закрыл очень маленькую часть проблем, по сравнению с legacy С++. По субъективным ощущениям, 5% примерно от общего количества. В основном это наследие С (например это).
Но добавил новых проблем не меньше, раз, два, три, и т.д...
Согласен, нужен RefCell, а это еще проверки.
А вот тут вы каким-то кривлянием пытаетесь проигнорировать ключевой вопрос:
Который кстати относится не только к Rust, но и к C# например (в нем есть ключевое слово unsafe).
Я хочу услышать простой ответ, да или нет.
Перечитал тред выше. Я с вами согласен в части:
- без unsafe написать двусвязный список можно, но будут дополнительные runtime проверки из-за реализации через слабые ссылки.
Но что мешает написать (или взять готовую из std) оптимальную реализацию через unsafe?
Вы возможно не поняли мой вопрос. Допустим есть оптимальная реализация (как на С) через unsafe без дополнительных runtime проверок.
Вы отрицаете что у нее можно сделать безопасное API?
Я правильно понимаю, что вы отрицаете возможность построения safe API поверх unsafe кода?
Переписали - это про конечные программы, утилиту командной строки например.
А здесь написали что-то новое.
Прямо над вашей цитатой написано следующее:
А под ней:
А в самой статье описана реализация интерпретатора Miri, который проверяет код std на соответствие модели Stacked Borrows:
Так что либо вы сами издеваетесь, либо мы по разному понимаем термин Aliasing Model.
P.S. Чисто ради интереса, минусы тоже вы лепите?
И вы еще раз подтвердили, что не переходите по ссылкам. Еще раз дублирую:
Конечно. Например я обратил внимание, что вы даже не перешли по ссылке оппонента. В ней 11-я статья от 2020 года описывает то, что вам нужно. А вы привели две статьи 2018 года, конечно в них всё было в зачаточном состоянии.
Это не правда. Вот этот комментарий, и особенно комментарии по всей ветке ниже показывают, что ваша претензия к реализации связного списка не справедливы. На Rust можно написать связный список без unsafe, но нельзя написать оптимальную реализацию в стиле С.
А вот в этом комментарии и нижележащей ветке вам привели ссылки на доказательства корректности модели памяти Rust. Но не доказательства корректности её реализации в компиляторе.
Да, конечно я имел ввиду библиотеку core, а не std.
Так и не лезут: компилятор допилили под требования Торвальдса, API выделения памяти допилили (чтобы возвращать OutOfMemory через Result, а также отделили библиотеку alloc от std). Вообще кучу всего в std доделали, и это будет полезно не только в Linux.
Сейчас Торвальдс требует опцию rustfmt, чтобы он не менял агрессивно форматирование блоков use. Звучит вполне логично, и это тоже сделают.
А Rust в ядре Linux есть и будет. И это мнение Торвальдса как раз основано на том, что его требования к языку и экосистеме учитывают и удовлетворяют тем или иным способом.
Работа с регистрами по определению unsafe, но в С и С++ нет такого ключевого слова, так что по кол-ву unsafe сравнивать некорректно.
Но поверх unsafe можно построить safe API, и этим повсеместно пользуются. Приведу простой пример:
Если взять стандартную библиотеку любого языка, то там 90% кода будет unsafe из-за системных вызовов, передачи указателей в ядро ОС, арифметики указателей ради быстродействия и т.д. Стандартной библиотекой пользуется любой программист на этом языке.
По моему опыту, скорость компиляции (инкрементальной) примерно одинаковая с С++. В работе с матрицами точно быстрее, где-то медленнее. В целом меня устраивает.
В последней статье правильно указали про lto, в текущем проекте (верхний крейт 40kloc) инкрементальная компиляция 4-5 секунд, но если выключить lto то 1-1.5 секунды.
Не инкрементальную делаю наверное раз в пол года, когда обновляю компилятор, так что вообще не важно. И в последней статье как-раз про то, как её ускорить на билд сервере.
Ну в С и С++ с этим как-то грустно, BetterC пока не пробовал.