Comments 153
- как со всеми этими владениями выглядит, например, процедура вставки в связный список?
- можно ли с карго использовать не гит, а, например, свн?
Со списком все просто — элемент передается списку во владение. По поводу не-гита — вопрос стоит несколько шире, но еще не решен.
Связный список технически можно написать на 100% safe Rust на умных указателях. Option<Rc<T>>
например. Проверку на None
придется везде делать явно, а все, кто ссылается на список, будут получать слабую ссылку (Weak
). Для разыменования слабой ссылки ее надо временно проапгрейдить до сильной. Если разрушить такой список, пока кто-то работает с его элементами (классический пример с remove под for each), объекты останутся во временном владении у тех, кто на них ссылается.
Но на практике так не делают. Для списков и вообще для хитрых алгоритмах на указателях, правильность которых доказана на бумаге, в Rust есть указатели в стиле Си (без проверки области видимости). Разыменование таких указателей возможно только с ключевым словом unsafe
. Слово unsafe
не отключает проверки, а просто добавляет пару слабо контролируемых компилятором возможностей. Но основное назначение этой штуки — не оптимизация алгоритмов, а FFI и обращения к железу, которые компилятор проверить не может. Они тоже считаются unsafe. Поскольку слово unsafe
в коде встречается редко (под ним будет всего несколько строк), проверить алгоритм вручную становится намного легче. При необходимости слово unsafe
можно вообще запретить в модуле или во всем проекте, благо алгоритмы вроде связных списков и все важные системные вызовы уже реализованы в стандартной библиотеке. В большинстве реальных программ слово unsafe
не встречается нигде или встречается только в интерфейсе к чужим библиотекам на C/C++.
Можно не пользоваться git, при этом cargo будет работать примерно как обычный cmake и все. В таком режиме можно использовать svn и что угодно еще. Если использовать git, появляются некоторые дополнительные возможности по работе с зависимостями.
Поскольку слово unsafe в коде встречается редко (под ним будет всего несколько строк), проверить алгоритм вручную становится намного легче
раст — это язык, в котором куча рассчитанных на новичков статей повторяет одну и ту же пропаганду. Кто, блин, проверяет код вручную в проектах больше хелловорлда? Правильно, никто. Не говоря уже про то, что причина возникновения ub вполне может прятаться в "safe"-коде. Это "магическое" unsafe абсолютно бесполезно, и по этой причине отсутствует в других языках
Так в том и дело, что весь код проверять и не надо. Только unsafe
блоки. В них обычно пишут больше всевозможных проверок и покрывают их тестами.
Я уже года 4 пишу на расте и за всё время мне unsafe понадобился всего пару раз. Это были переписывания с C/C++ на раст и такой код был временным клеем. Как только последняя функция была переписана на расте — он удалялся. И в чистом коде на расте никакого unsafe уже не было.
Ну и важно понимать, что даже unsafe раст намного безопаснее C/C++. У компилятора есть проверки которые он выполняет всегда (safe и unsafe) и проверки которые доступны только в safe. Это всего лишь означает, что компилятор не может сделать это сам и требуется помощь человека (обычно это означает, что нужны тесты для этого куска кода).
Глобальные переменные в прикладном программировании не нужны.
Тоже не нужны. https://crates.io/crates/cmim
Эти unsafe'ы можно завернуть в безопасный интерфейс. См. например: https://docs.rust-embedded.org/book/concurrency/index.html#global-mutable-data
Это больше всего напоминает запихивание мусора под ковёр во время уборки.
В смысле — "кто знает"? Откройте их исходный код да посмотрите!
Те самые мифические программисты, которые всегда на Си пишут без ошибок, уж точно должны быть способны один раз написать безопасные обёртки для стандартной библиотеки Rust.
Лично я — часто. Но в случае с распространёнными библиотеками можно с некоторой вероятностью положиться на сообщество.
А вот часто ли сишники инспектируют свои зависимости?
А вот часто ли сишники инспектируют свои зависимости?
Лично я — всегда. В тех областях где применима "сишка", это просто перетекает в обязанность. Я просто не могу применить в Embedded код о котором я ничего не знаю. Например, сколько он жрёт памяти, есть ли там динамические аллокации и т. д.
На плюсах в моей области та же ситуация. Нужно ли мне тянуть Boost ради Event-loop или я напишу его сам для трёх дескрипторов на kqueue/epoll.
На Python кстати вообще почти не тяну левых библиотек, т.к. у него на мой взгляд одна из лучших стандартных библитек, которая покрывает мои запросы на 90%.
Я искренне желаю, чтобы инфраструктура Rust не повторила ошибок Node.js и NPM.
Это гарантирует, что если API библиотеки выполняет контракт, то memory safety будет независимо от способа использования этого API.
Хотя это не обеспечивает абсолютную безопасность, это дает намного бОльшую безопасность, чем написание в стиле Си, где вообще весь код unsafe. Проверить вручную один раз пять простых строк в safe-обертке намного легче, чем дебажить каждый клиентский проект.
В хороших библиотеках на С в свою очередь документируется стратегия владения объектами (потому что иначе никто не сможет это использовать) и это в обязанностях программиста следовать им.
Что такое, в вашем понимании, "скрытый контракт"?
В хороших библиотеках на С в свою очередь документируется стратегия владения объектами (потому что иначе никто не сможет это использовать) и это в обязанностях программиста следовать им.
А в Rust эта задача возложена на компилятор. Не совпали ожидания — код просто не скомпилируется.
Не используйте библиотеки и обертки, которые недостаточно протестированы и в которых может твориться настоящий ад из утечек и повреждений памятиэтот совет по сути сводится к использованию только тех библиотек, код которых вы лично перепроверили.
При этом, если писать на C++ такие же обычные проекты, избежать ошибок памяти без опыта использования современных фич C++ очень сложнобез опыта на любом языке тяжело писать.
а избежать UB вообще считай что невозможно.а вот тут можно вдаваться в очень глубокую демагогию. Дело в том, что стандарт с++ очень явно определяет какое поведение определено стандартом, какое реализацией, а какое не определено совсем. Соответственно, кусок кода, не содержащий UB, обязан быть корректным. Однако спецификация rust куда менее строгая, большая часть описанных в ней гарантий покрывает именно safe код, и формально практически любой код с unsafe является UB.
Надо заметить, что это «очень умное множество инвариантов» (ОЧЕНЬ УМНОЕ МНОЖЕСТВО ИНВАРИАНТОВ). Вынь любой из них — и вся Rust Safity посыпется.
Так вот проблема таких unsafe-блоков (как я её вижу) в том, что:
1.) unsafe-блок — это «вот в этом коде мы нарушили инварианты. Реальная ошибка может проявиться где угодно далее.
2) Пользователю предлагается „на коленке“ проверить и поверить, что его программа, хоть и нарушает Rust-safe инварианты, всё ещё сохраняет safity (просто из-за того, что его программа частный случай) — это весьма хлопотно.
2.б) Пользователю предлагается не просто однократно поверить, а как правило поверить на всё дальнейшее время существования программы, что unsafe-блок останется безопасным, как бы не менялась safe-часть его программы.
Вообще недоверие к пунктам „2“ и „2.б“ по-моему вполне оправдано.
проверить, что его программа, хоть и нарушает Rust-safe инварианты, всё ещё сохраняет safityЭто слабый вариант. В идеале,
1.б) автор unsafe блока должен проверить, что unsafe block будут сохранять safity в контексте любого safe кода, а не только в частном случае конкретной программы.
Особенно если этот unsafe блок находится в библиотеке. Если сильный вариант проверки выполнен, пользователь может не волноваться и 2) и 2б) не требуются.
Ну можно впасть в крайность и разметить весь код как unsafe. И даже в этом случае получится язык более безопасный чем C/C++.
Процитирую мануал. В unsafe коде можно дополнительно делать:
- разыменовывать указатель
- вызывать другой unsafe код
- обращаться к глобальным переменным
- реализовывать unsafe traits
- использовать union-ы
Всё! Все остальные инварианты и проверки сохраняются, в том числе и пресловутый borrow checker.
Для списков и вообще для хитрых алгоритмах на указателях, правильность которых доказана на бумаге, в Rust есть указатели в стиле Си (без проверки области видимости).
Зачем тогда нужен Rust, если правильность алгоритмов доказана на бумаге, а не автоматически? Здесь подход Rust мог бы быть огромным плюсом, предоставляя автоматическую проверку корректности алгоритмов после изменений, но указатели в стиле С как раз такую проверку отключают.
Затем, что в большинстве случаев правильность доказывается автоматически, и только для ничтожного процента кода ее приходится доказывать вручную.
Полностью автоматическое доказательство правильности в любом случае принципиально невозможно из-за проблемы остановки. Rust решает проблему остановки тем, что проверяет автоматически большинство алгоритмов, а не все. Для оставшегося меньшинства программисту на выбор предлагается два варианта: перенести проверку на runtime или провести некоторые действия с неполной проверкой.
Я пишу на Rust уже 7 лет, у написанной мной библиотеки 2.2 млн. скачиваний, и за все это время мне пришлось поставить под unsafe в общей сложности меньше сотни строк. Причем в подавляющем большинстве — не алгоритмы, а просто вызовы сишных библиотек.
Эй, почто человека минусуете? Если у компьютера конечное число состояний, то компьютер с достаточно бо́льшей памятью может построить полный граф переходов между состояниями и найти в нём все циклы.
В теории — да, а вот на практике понадобится слишком много памяти. Это во-первых.
А во-вторых, сама идея доказывать правильность работы программы исходя из той посылки, что на компьютере где программа будет работать будет не более X памяти отдаёт чем-то странным. Пройдёт время, появятся компьютеры с большим объёмом памяти — и что тогда? Правильный алгоритм должен иметь ограничение на используемую память снизу, а не сверху.
У компьютера с 64кбайт памяти 2^524288 состояний, машина Тьюринга с 6-ю состояниями может проработать 10^36536 шагов перед тем как остановится.
Не в нашей вселенной такое перебирать.
Для конечной машины всегда возможно.
"Принципиально невозможно в любой вселенной, имеющей ограниченные вычислительные ресурсы". Так пойдёт?
Так в какой вселенной будем анализировать? В нашей? Не выйдет. В той, в которой выйдет? Там можно взять машину Тьюринга с 7-ю состояниями. И тоже не выйдет.
Верно, но немного лукаво. Только часть доступной памяти участвует в реализации алгоритма и конечность многих конструкций доказывается формально. Это позволяет значительно снизить вариативность, на чем мы люди и держимся :)
Мы, люди, держимся на том, что не пытаемся решать проблему остановки (точнее, обойти теорему Райса). Мы не берём произвольный алгоритм, и потом пытаемся доказать, что он решает нужную задачу. Мы конструируем алгоритм под задачу. Правда баги и CVE показывают, что алгоритмы сконструированные таким способом обычно решают не совсем ту задачу.
Исходного посыла это не меняет. Невозможно (ни практически, ни теоретически) решить проблему остановки. Тут помогут только супер-Тьюринговые компьютеры, которых не видно даже на горизонте.
А в каком контексте проблема остановки связана с операциями на списках, которые вы упоминаете выше? Можно подробнее в деталях?
По существу, проблема остановки решаема для linear-bounded automaton. Ну и формальная верификация алгоритмов для списков (и вообще для арифметики на указателях) при помощи компилятора уже существует, просто Rust так не умеет:
* ats-lang.sourceforge.net/DOCUMENT/INT2PROGINATS/HTML/HTMLTOC/c3321.html
* ats-lang.github.io/DOCUMENT/INT2PROGINATS/HTML/x3828.html
Проблема остановки лишь приводит к необходимости иметь в языке "unsafe" для самого общего случая. Частный случай со списками возник по другой причине, а "unsafe" для него использовали потому, что он все равно уже был в языке.
Проблема верификации коллекций в Rust вытекает из искусственного ограничения, введенного из соображений эргономики: при верификации обращения к модулю запрещено заглядывать внутрь модуля, модуль следует рассматривать как черный ящик и смотреть только на написанные человеком сигнатуры функций. Это сделано для того, чтобы изменения внутренностей модуля не приводили к непреднамеренным изменениям его свойств с точки зрения клиентского кода.
Код указателей и прочих unsafe-действий тоже верифицируется, но менее строго. Например, в stable rust до последнего времени не было способа сказать, что приходящий из чужого модуля указатель никогда не бывает нулевым (теперь есть, NonNull
называется). Пока нет в общем случае способа сказать, что указатель на объект из коллекции не затрагивает другие объекты (единственное, что можно сказать — что это указатель на объект, принадлежащий другому объекту). В подобных случаях мы пока что просто говорим компилятору "поверь нам на слово, что так делать можно". Либо добавляем проверку на рантайме в надежде на то, что оптимизатор обнаружит инвариант и выкинет эту проверку.
Пример со списками: если написать модуль для списков, то есть способ сказать компилятору, что функция push()
как-то изменяет содержимое списка, но нельзя сказать, что она не инвалидирует указатели на уже имеющиеся элементы. Хотя компилятор в принципе способен это вывести сам, он не имеет права, так как в сигнатуре функции мы этого не сказали, и, возможно, в будущем планируем поведение функции push()
изменить.
Возможно, с точки зрения теории это не очень красивый способ, но это лучше, чем получить теоретически правильный язык, которым будет слишком трудно пользоваться для массового применения.
ок, значит имплементации разных списков и unsafe в них — не про HP, и теоретически нет непреодолимых препятствий проверять их безопасность компилятором — есть проблема эргономики, о которой вы говорите ниже.
Но тогда у меня есть философский вопрос: если Rust позиционирует себя относительно С/С++ как язык, который позволяет предотвращать проблемы безопасности по памяти и продвигает себя в аудитории, для которой эти проблемы должны быть важны и насущны, то почему в этом позиционировании присутствует вопрос эргономики, который изначально субъективен и не направлен на обобщенное решение проблем с памятью?
Некоторые вещи в мире действительно сложны и требуют изучения, неэргономичность математики, например, не является аргументом в пользу введения в нее необобщенных построений.
То есть, если я тот инженер, перед которым стоит задача решить проблемы безопасности софта по памяти, мне не очень понятен аспект эргономики, ради которой приходится жертвовать безопасностью в некоторых случаях, особенно если эти случаи распространены в низкоуровневом коде ядер ОС и драйверов. Для меня как для инженера более логично выбрать инструмент, который решает эту проблему обобщенно, без оглядки на эргономику. А уже когда эта основная задача покрыта, можно подумать об эргономике.
Потому что выйдя за определенные рамки эргономики мы покидаем аудиторию, для которой изначально и предназначался язык и безопасность программ которой он и был призван улучшать. У Раста нет цели быть принципиально 100% безопасным в ущерб остальному, у него цель быть как можно безопаснее в заданных ограничениях, коих еще вагон.
Мне кажется тут разумнее упомянуть теорему Райса, а не проблему остановки.
Хотя вывод примерно тот же
Ну и фреймворки типа Frama-C помогают писать спецификации прямо в коде.
Немного оффтоп. Вы 7 лет пишете коммерческий софт? Если да, то расскажите немного подробнее, пожалуйста.
Потому что Rust это практичный компромисс между полным отсутствием проверок (С/С++) и слишком сложными и медленными системами доказательств.
И, как показывает опыт, компромисс вполне удачный. Этого самого unsafe кода исчезающе мало по сравнению с остальным кодом.
Связный список технически можно написать на 100% safe Rust на умных указателяхно будут дополнительные расходы на подсчет ссылок.
В большинстве реальных программ слово unsafe не встречается нигде или встречается только в интерфейсе к чужим библиотекам на C/C++.на этот счет есть какая-то статистика? А то утверждение кажется чересчур амбициозным
У меня нет статистики. Но мне кажется позиционирование Rust как убийцы C/C++ не совсем верно. Он скорее приходит в область где правили Go и Java. Там где рулили языки с GC. А теперь появилась альтернатива с высокой производительностью и богатыми возможностями.
А вот в каком-то Embedded usafe кода просто не избежать.
на этот счет есть какая-то статистика? А то утверждение кажется чересчур амбициозным
Есть. Оценка там довольно грубая, но по ней выходит, что около 70% крейтов не содержат unsafe-код вообще и что в целом 95% кода, размещенного на crates.io, является safe.
Есть отличная статья по теме списков
Альтернативная реализация c unsafe-кодом в репозитарии RustAlgorithms: ссылка на linked_list.rs
И реализация без unsafe-кода: leviathan88/safe_lists_rust
Как мне кажется, в расте, для реализации различных структур, ноды которых имеют ссылки на другие элементы. Наиболее идиоматичным, будет вариант с использованием списка с индексным доступом вроде Slab или Vec и хранение его ключей как ссылок на связанные элементы.
use slab::Slab;
type NodeID = usize;
pub struct LinkedList<T> {
head: Option<NodeID>,
tail: Option<NodeID>,
elements: Slab<Node<T>>,
}
struct Node<T> {
prev: Option<NodeID>,
next: Option<NodeID>,
data: T,
}
Для простой структуры, вроде двусвязного списка, данных подход может показаться переусложнённым. Но она уже реализована и оттестирована в стандартной библиотеке. А в качестве "простой" альтернативы, можно использовать счетчики ссылок (Rc/Weak), или окунутся в C-like код с сырыми указателями. Кстати, в std двусвязный список реализован именно на них.
https://www.areweguiyet.com/ — вот тут собирают информацию о состоянии GUI в Rust.
Из личных наблюдений — есть очень приличный gtk-rs (привязка к gtk3), куча обвязок вокруг WebView, и написанные с нуля библиотеки со скупыми наборами виджетов.
Я пробовал байндинги к gtk3, qt, cocoa, winapi. Самым беспроблемным с точки зрения соответствия оригинальной документации являлся, внезапно, winapi — можно код на си напрямую переписывать в Rust (естесственно, завернув в unsafe). Следом идет cocoa, который мог бы быть первым, но для замыканий и другого асинхронного кода там намутили, разумеется, забыв задокументировать. Qt слегка наркоманский в плане API, но если понять принцип, становится несложно, но многословно. Gtk прекрасно используется, пока вы используете стандартные контролы, но стоит отойти в кастомизацию или расширение стандартного функционала, начинаются приключения — документации нет, иногда вырезана поддержка в коде (кастомные сигналы).
Ещё Flutter как вариант. Хотя думаю смысл раста в нём только если нужно писать куски кода для доступа к нативному API системы, вместо C/C++.
www.areweguiyet.com — вот тут собирают информацию о состоянии GUI в Rust.
Думаю вот эта ссылка будет куда полезней.
Пока пользуюсь Druid, на мой вкус выглядит бомбезно, но уж больно непривычно, если до этого не писать на каком-то другом фреймворке с парадигмой триады и не прописывать вручную весь лайфцикл приложения.
Всякие примочки типа вывода динамических слоёв для однооконного приложения (показ Toast'ов, модальных алертов внутри окна) пришлось писать самому.
Для immediate mode есть биндинги к imgui, но т.к. стояла задача кроссплатформы без внешних либ вообще, то не стал их использовать.
Там автор потрудился попробовать несколько гуишных крейтов и поделился своими впечатлениями.
Быстрее на анбоксинг, соответственно если в шарпе анбоксинг уже решён то эквивалентен. Вопрос только что делать с исключениями, но это и для шарпа проблема.
Вопрос не совсем корректен, тк не относится конкретно к расту. Rust генерирует бинарники в формате целевой платформы, например ELF на линуксе, и этот бинарник обычным образом вызывает код из динамической библиотеки, через Posix С ABI. То же произойдёт если бинарник написан на си или на плюсах. ЕМНИП первый вызов будет более медленный (оверхед на поиск функции в таблице PLT), остальные быстрее.
Так что вопрос здесь "скорость вызова DLL в C# и в компилируемых языках". Не знаю правильный ответ на этот вопрос, интуитивно кажется что компилируемые языки делают вызов из DLL быстрее. Но вот обратный пример с luajit. Там есть ссылка на проект на гитхабе с бенчмарками различных языков при вызове .so кода, шарп там медленнее компилируемых языков. Короче всё зависит от механизма вызова.
Rust не добавляет дополнительного оверхеда к таким вызовам. Скорость будет неотличима от C/C++ на компиляторе с тем же бэкэндом и аналогичном линковщике.
Потому что системные потоки фундаментальные. На расте можно сделать гринтреды средствами языка, в обратную сторону это не сработало бы.
Если вкратце, разработчики Rust решили что это язык для максимально эффективного системного программирования и должен иметь минимальный runtime.
Так же думаю, что зеленые треды мешают эффективному FFI с C. Например в Go любой вызов C функции это относительно дорогая операция.
Правильнее будет спросить, что сейчас сделалось с green threads. В Rust появилась библиотечная реализация фьючерсов, причем не требующая переключать стэки, с нулевым оверхедом, а потом на ее основе сделали async/await. Все это делает зеленые потоки в большинстве задач ненужными. Также появилась библиотечная реализация классических зеленых потоков. Все это позволило обойтись без встроенной в язык поддержки.
Поддержку системных потоков язык обязан иметь потому, что компилятор должен уметь проверять корректность передачи данных между потоками. Хотя сама реализация системных потоков тоже в библиотеке, язык знает, что такое "исполнение в другом потоке" и "thread-local".
Как-то приелись мотивашки по расту и статьи со введением в Rust, их уже наштамповали мильён, а вот материалов более высокого уровня наоборот не хватает, из стандартных только rust reference и nomicon.
MSRC Security Research 2019 года (Microsoft) показал, что 70% Common Vulnerabilities and Exposures (CVEs) в их продуктах были связаны с проблемами безопасности памяти в С и С++ (думаю весьма известный график):
В C и в C++ нет никаких проблем с безопасностью памяти, поскольку malloc() и free() — это всего лишь обертки системных вызовов в ядро. Каких-то более «безопасных» системных вызовов управления кучей нет. При этом никто не мешает на их основе создавать субаллокаторы с проверками и прочим, тем более, что С++ предоставвляет такую возможность для всех своих контейнерных классов.
Проблема «безопасности памяти» не в языках, а в программистах. Из всех перечисленных выше «проблем» за всю свою долгую жизнь в C и С++ и даже в ассемблере не помню ни одной, с которой пришлось бы столкнуться. Налететь на них можно только если сильно спешить и писать код напрямую с головы, не предваряя кодирование проработкой алгоритмов, протоколов обмена объектами и прочим, что должно прорабатываться до тогшо, как начнет создаваться код, котороый увидит свет.
Но как и в любой другой работе, зачем её делать вручную, если можно делать не вручную?
Всякие сишарпы, джавы и джавоскрипты популярны стали отчасти потому что там с этим всем заморачиваться не надо, а оверхед от технологии, позволяющей этим не заморачиваться, для большинства задач прикладного программирования минимален. С растом же это приходит и в системное программирование.
Но как и в любой другой работе, зачем её делать вручную, если можно делать не вручную?
Вот и я так думаю. Зачем вручную воевать с borrow checker, лайфтаймами, элементарные вещи делать через стандартную библиотеку.
У меня конечно субъективный взгляд, т.к. пока не занимался детальным изучением Rust. Пока просматривая код интересных мне проектов на нём, нахожу синтаксис Rust крайне не интуитивным. Надеюсь это пройдёт.
Зачем вручную воевать с borrow checker, лайфтаймами, элементарные вещи делать через стандартную библиотеку.
Это рассуждение из разряда "зачем заморачиваться с техникой безопасности и так всё нормально будет, мешает только". Правда программисты стреляют в ногу только метафорически, и не в свою ногу, а в пользовательскую.
Что такое "своя семантика владения"? По вашей ссылке что-то странное написано.
> По вашей ссылке что-то странное написано.
а вот так лучше?
Да тут с обоих сторон, похоже, фанатиков хватает, мне тоже кто-то плюнуть в карму успел, при том что ни единого ответа по существу ни от кого, кроме вас, не было ¯\_(ツ)_/¯
Возможен ли язык, который лучше раста по всем направлениям? Конечно, возможен, но я о нем пока не слышал. Более того, по моему мнению, для многих задач Rust является лучшим языком в совокупности на данный момент, лучшим выбором в целом, так сказать. И число таких задач продолжает увеличиваться. Haskell и ко тоже круты, но Rust выходит сильно практичнее.
Ну так и пишите на ассемблере сразу, зачем заморачиваться с чем-то вообще, если можно всё сделать интуитивно и просто?
На практике воевать приходилось от силы первые часа два, пока не пришло осознание принципов не на уровне "прочитал доку", а на интуитивно-прикладном. После этого, в свою очередь, перестал творить всякое и терять аллокации/деаллокации/прочие приколы на обычном си — но начала пухнуть башка с него от того, что всё, что в расте делается неявно и автоматом, тут нужно держать в голове и прописывать.
С синтаксисом да, беды есть, т.к. авторы и сами заявляли, что затачивали его под более простой парсинг, чем под лёгкую читабельность. Но после пары проектов вызывает дискомфорта куда меньше, чем те же плюсы (при условии, что проекты не уровня хелловролд, а именно полноценных с дженериками и прочими темплейтами) — опять же, сугубо субъективно.
Ну так и пишите на ассемблере сразу, зачем заморачиваться с чем-то вообще, если можно всё сделать интуитивно и просто?
Нет, ассамблер не интуитивен. Ну и мне кажется интуитивность это очень важное свойство языка программирования. И да, я понимаю, что не всё и не всегда можно сделать простым и интуитивным. Но я считаю, что замена С/С++ должна быть сходу доступна программистам на оных, без изучения теории типов и других концепций.
После этого, в свою очередь, перестал творить всякое и терять аллокации/деаллокации/прочие приколы на обычном си — но начала пухнуть башка с него от того, что всё, что в расте делается неявно и автоматом, тут нужно держать в голове и прописывать.
Я никогда в Embedded и в разработке модулей ядра ОС не теряю аллокаций. Потому что в первом случае их вообще нет, либо как у Rust unsafe — 0,0001%. Во втором их тоже две-три штуки. А дальше, дальше не нужно на Си писать микросервисы.
С синтаксисом да, беды есть…
Не стал вас цитировать дальше, т.к. абсолютно согласен. Современные плюсы на мой взгляд тоже не читаемы, особенно если обмазаться шаблонами.
Я не против Rust, просто пока больно, надеюсь пойму его концепции.
У каждой сущности есть владелец — вся концепция Rust.
Хочешь ссылку на сущность — имей в виду ее владельца. Чтение, запись — согласно принципам общего доступа, знакомым каждому по работе с многопоточностью, только здесь доступ проверяется статически во время компиляции. Да, нужно решать все проблемы сразу, иначе код просто не соберется, но во первых, с опытом правильный код пишется с первого раза, а во вторых, это снимает кучу головняка с техподдержкой.
Нет, ассамблер не интуитивен.
Опять же, вкусовщина, по мне Z80 асм проще в изучении, чем средний бухгалтерский калькулятор. А вот в х86 таки да, недоперемудрили, если ещё и лезть в MMX/SSE/2/3/4 :-) Даже на Altivec как-то попроще казалось. Но да ладно, это уже всё флуд.
Я никогда в Embedded и в разработке модулей ядра ОС не теряю аллокаций.
Ну вот, это просто ваш частный случай, где оно и не нужно.
не нужно на Си писать микросервисы.
А почему бы и не писать? Но вот на чистом Си оно больно и трешово, а на Расте элементарно, при том что производительность примерно та же.
У меня лично Rust занял весьма широкую нишу, если можно так выразиться, в геймдеве, а именно написании альтернативных серверов к играм. 120 активных юзеров, поделка на ноде жрала под пару гиг оперативки, на расте хватает пары сотен метров. Либо эмулятор лицензионного ключа, чтобы не пилить оригинал лишним использованием — тут уже всё чисто даже на стандартной библиотеке делается, т.к. удобные сокеты есть, строки есть. Всякие лоадеры и патчеры тоже — можно, конечно, писать на том же сишарпе, но это будут вагоны врапперов поверх WinAPI и прочего треша, можно писать на Си — но банально неудобно.
Плюс элементарнейшая кроссплатформенность. На си приходилось думать, как и куда что обернуть для этого, на расте просто делаешь cargo build
, разве что докинув тулчейн для кросс-компиляции. В итоге пишешь сервер на винде на машине с игрой, а деплоишь на линуксовую в один тык. Или пишешь на современной винде, а потом деплоишь на эмбеддед винду XP, правда уже с нюансами — там стандартная библиотека неполная, нельзя мьютексы и ещё что-то использовать. Но сам факт, что по простоте сравнимо с той же джавой, для меня был решающим.
Изучением системы типов тоже не занимался, просто начал писать код сразу, и как-то оно само дальше в голову залетало. Оно уже потом заинтересовало, когда стало ясно, насколько это удобно, когда код либо работает, либо даже не собирается :-)
Немного ещё позанудствую, malloc и free это библиотечные функции, которые перераспределяют память которую ядро одним куском выдало.
В C и в C++ нет никаких проблем с безопасностью памяти
Да-да, а эти самые CVE враги выдумали?
При этом никто не мешает на их основе создавать субаллокаторы с проверками и прочим, тем более, что С++ предоставвляет такую возможность для всех своих контейнерных классов.
Вот только такие проверки придётся делать в рантайме, т.е. на них будет тратиться время.
Проблема «безопасности памяти» не в языках, а в программистах.
Ну а недоступность идеальных, никогда не допускающих ошибок программистов — уже проблема языка.
Huawei делает много интересного, возможно, появятся вакансии, совмещающие интересный язык с интересной сферой деятельности.
это не всем интересноНе интересен блокчейн — не подавайте резюме на блокчейн-вакансию, вас же не заставляют. А если хочется работать именно в Huawei, но не с блокчейном — ну простите, они не будут подстраиваться под конкретно ваши желания.
Я никого не заставляю подстраиваться. Просто спрашиваю.
Если Huawei планирует тоже использовать rust только для блокчейна — ок, все понятно.
Но спросить то можно?
Интересно, для чего huawei планирует использовать rustДля чего угодно.
Просто, абсолютное большинство вакансий — это какой-нибудь блокчейн, что не для всех интересно.Ну и ладно, не интересно так не интересно.
Почему вы вдруг решили, что Rust у них должен пойти только в новые проекты и от новых людей? У них и своя мобильная ОС есть, и оборудование они делают, и мало ли что ещё.
Если ваше мнение — то и так понятно, что на расте можно писать (почти?) все и что он может применяться и на старых и на новых проектах.
Просто, если такой крупный вендор будет использовать раст для разработки мобильной ОС, для оборудования — что угодно за пределами блокчейна — это большая новость для раст сообщества.
Понятно, что даже если блокчейн — все равно большая новость, да и блокчейн многим нравится, но потребность в не-блокчейн проектах на расте довольно большая.
Поэтому, хотелось бы получить ответ от автора.
разве я говорил, что нельзя? Просто, это не всем интересно и регулярно встречаю обсуждения «а что есть, кроме блокчейна».
Есть HFT :) Бувально на следующей неделе планирую пособеситься в очередную контору
А зачем это описание нужно? Неужели найти эту информацию самостоятельно так сложно?
Да, я зануда.
Тот же python: «Python 3.8.8 cannot be used on Windows XP or earlier» чем не требование.
Сколько надо выделить виртуальной машине ресурсов что бы оно завелось и компилировало.
Тот-же си очень скромные требования имеет и есть в огромном количестве исполнений.
или уже много альтернатив естьТак а какой вам конкретно инструмент потребовался? Только сам rustc? Или вместе с cargo? Требуют ли ваши крейты компиляции каких-то внешних библиотек (обычно это при сборке для Windows, но тем не менее)? Под какую платформу вы планируете собирать (компиляция в WASM иногда съедает гораздо больше памяти, чем в нативный код)? Разрабатывать хотите на той же машине, или вам только для сборки интересно? Если разрабатывать — будете ли вы Language server использовать? Если да — RLS или Rust Analyzer? Набор инструментов, может, и один, но переменных в уравнении «хватит — не хватит» предостаточно.
Python 3.8.8 cannot be used on Windows XP or earlier
Зато есть MicroPython, который аж в микроконтроллерах работает.
Почему вы должны попробовать Rust
- Память
- Многопоточность
- Тестирование
- И документация
И это всё?
Знаете, с такой рекламой я лично языком не заинтересуюсь. Другого жду от языка.
Лично я заинтересовался Rust'ом после того, как он начал на хабре регулярно всплывать в комментариях к ФП-шным постам. Про раст услышал году так в 2015 как "про тот сложный язык язык с владением, про который на хабре пишут статьи о том, как строку в функцию передать". А заинтересовался после всех этих трейтов, недо-монад Option и прочих типов-сумм.
Другого жду от языка.
Например?
Но вообще да, все такие статьи страдают тем, что не очень убедительно повторяют одни и те же тезисы. С другой стороны, даже такие "мелочи" как тестирование или документация, когда их распробуешь, начинают цениться намного больше. Есть достаточно инструментов для документирования кода не привязанных к языку, но когда есть один (и главное удобный!) способ причём документация автоматически проверяется при тестировании (опять же — без дополнительных приседаний!), то это качественно другой уровень. Мне кажется, что именно поэтому даже не у самых популярных растовых библиотек есть неплохая документация. С фрейворками для тестирования похожая история.
Лично для меня "килер-фичи" раста это" бороу чекер, явный ансейф и как раз "инфраструктура". Ну и то, что это "современный язык" во всеми прилагающимися штуками вроде паттерн матчинга.
В подавляющем большинстве случаев сегодня нужно максимизировать трудозатраты, Rust достаточно сложный язык и уже по существующим репозиториями вижу что код который на нем пишут будет вызывать не меньше трудностей с пониманием чем с++ и т.п.
Все описанные в статье фичи это хорошо. Но не самое важное в большинстве случаев как я писал выше. Поэтому у меня сомнения что есть перспективы кроме системного программирования.
Согласен, что трудозатраты на обучение больше чем на другие известные мне языки, но не трудозатраты на написание кода! Обучение действительно довольно сложное, но если понять основные принципы (а они не сложные и их довольно немного!) и научиться их применять в коде (тут уже будут проблемы), то именно процесс решения конкретной задачи идет намного более гладко по сравнению с.
Я, например, чувствую себя с Rust намного более эффективным чем с С++, и возвращаться не желаю.
А потом выскакивает ошибка типа «already borrowed» и все. Там можно неделю убить.
Уточнение: убить кучу времени можно не на поиск самой ошибки, а на поиск способа её обхода. И да, неделя — всё равно много как-то.
Мне надо смоделировать участок цепи и распространение энергии от источника к потребителям, все сделано так, что есть источники в которые как
Rc<RefCell<dyn IConsumer>>
вставляются потребители, источники потом сами дергают методы у IConsumer которые в свою очередь тоже имеют Rc<RefCell<dyn IConsumer>>
(или их vector) что делает дерево.Среди потребителей есть контакторы (свичи) которые могут выбирать активный источник, и вот этом дереве я получил «already borrowed» я не смог у себя в голове развернуть все это дело и понять где ошибка, кроме как спустя некоторое время догадался, что какие-то контакторы в дефолте стоят в не верных положениях. Итого: замечательно то, что благодаря этой ошибки можно понять, что цепь имеет не верные соединения, но очень тяжело понять где именно, я себя реально первый раз в расте ощутит беспомощным.
ПС пока писал этот пост я кажется понял как конкретно в моем частном случае искать где проблема, не буду грузить лишним текстом, лишь скажу, что в общем ситуация не сильно улучшилась, но сузилось количество мест для проверки и поиска.
Но даже не смотря на все это, я на с++ больше писать не хочу, и не буду, только в legacy.
А неделя я думаю просто утрирование.
Это самое место не надо искать, его сообщает компилятор.
Rc<RefCell<dyn IConsumer>>
А, ну да, RefCell. RefCell — это такая штука, которая переносит проверки в рантайм. По возможности лучше её не использовать.
Там вон где-то выше по сообщениям напомнили про идею индексов и хранению объектов в другом месте и получать доступ по индексам, буду на днях думать можно ли перепроектировать все на такой подход.
В целом после перехода с С++ на раст, очень большая проблема в том чтобы наработать паттерны для решения задач именно на расте, если паттерны которые были наработаны на с++ плюс минус можно использовать в других языках, то в раст надо искать свой подход с ноля :( и зачастую искать самостоятельно путем проб и ошибок.
Индексы — глупость. Лучше уж сделайте у своих потребителей внутреннюю изменяемость, то есть объявите их поля как Cell. Тогда внешний RefCell можно будет убрать.
struct AConsumer {
consumers: Cell<Vec<Rc<dyn IConsumer>>>
}
impl AConsumer {
fn process(&self) {
let borrow = self.consumers.take();
for v in &borrow {
...
}
self.consumers.set(borrow)
}
}
А еще то, что теперь при неверном соединении цепи, не будет никаких ошибок, но при этом цепь может спокойно оказать в не валидном (с точки зрения предметной области) состояние молча. Хмм… что-то мне подсказывает, что наверное лучше тогда оставить как есть сейчас, т.к. предложенный вами подход замаскирует проблему.
Если вдруг кто не понял почему, то при повторном попадание в эту функцию когда она еще не завершена при первом вызове, приведет к тому что Cell::take() вернет пустой массив (т.к оригинальный массив забрал первый вызов), и ничего не произойдёт, тогда как с RefCell второе попадание отловится рантаймом и выпадет паника (ну или нет если вы будете обрабатывать это самостоятельно с try_borrow()).
В любом случае спасибо за напоминание, что cуществует еще и Cell.
Не знаю у кого индексы — глупость, арены — типовое решение проблемы раста с расшаренным мутабельным доступом.
Я частенько пытаюсь попробовать новый язык, и даже прочитал пол доки по Rust. Правда в итоге я так и не понял где же можно его использовать с преимуществом над c, python или тем же JS.
Та же, например, Ada без освобождения памяти (а так обычно обстоят дела в авионике) гарантирует memory safety и thread safety (с использованием
protected
), и при этом обладает стабильной спецификацией. MISRA-C позволяет уменьшить число неявных ошибок программистов C и т.д. С точки зрения разработчиков и заказчиков таких систем, плюсы от перехода на rust не столь очевидны.Мой личный опыт показывает что большая вариативность — это скорее минус чем плюс, под вариативностью я понимаю «17 способов инициализировать переменную в C++» В с++ такое везде, начина от компиляторов и систем сборок заканчивая самим языком, везде есть из чего выбрать, а на деле весь этот зоопарк вносить только хаус и проблемы. То что планировалось как гибкость на любой случай жизни превращает многое в ад. Меня до сих пор бросает в дрож когда я думаю о том, что мне надо пойти и создать проект для новой библиотеки на с++, а если мне еще и зависимости внешние нужны, нет, для меня лучше пусть будет одна система с чем то вроде «cargo new my_lib --lib».
Опять же я понимаю, что в других сферах этого может быть не достаточно, но прежде чем добавлять вариацию по моему мнению надо очень хорошо подумать.
сейчас такого рода вещи на с/c++ обычно делают
Ничего слишком серьёзного писать на нём не приходилось, но все мои «экспериментальные» программы без проблем собирались и работали и на Windows, и на Linux.
Единственное, с чем столкнулся: некоторые/многие крейты, связанные с GUI, могут не поддерживать всю тройку ОСей.
Есть вариант сдк написать под все платформы, локальная базы + сеть для пересылки
вот и думаю может кор залячкать на rust… )
но нужно чтобы это было официально поддерживаемо, а не колдовство )
потому как могут и сжечь ))
zen.yandex.ru/media/nuancesprog/rust-i-razrabotka-krossplatformennyh-reshenii-dlia-mobilnyh-ustroistv-5f4b737fbf0b1c1f8aac4617
пугает немного, что написано в разделе experiments )
mozilla.github.io/firefox-browser-architecture/experiments/2017-09-06-rust-on-ios.html
mozilla.github.io/firefox-browser-architecture/experiments/2017-09-21-rust-on-android.html
А как у Rust с кроссплатформенностью? Можно и собрать бинарники для Windows/FreeBSD/Linux в, скажем, OpenBSD? Или придется сотни toolchain'ов искать и ставить непонятно откуда?
Расскажите пожалуйста об областях применения на практике
Например, что насчёт создания HTTP API.
Или создания микросервиса, который бы слушал из и писал в Kafka.
Спасибо
Про HTTP — смотрите сюда: https://actix.rs/docs/getting-started
Для Kafka есть обертка над librdkafka: https://github.com/fede1024/rust-rdkafka
Области применения весьма широкие. Например, вот что делал я на Rust:
- Web-сервисы: http, gPRC, WebSocket (в том числе и общался с Kafka). Как крупные web-приложения, так и небольшие.
- Web-сервер.
- Web-клиенты.
- Фронтэнд под WASM в браузере.
- Внебраузерные WASM-модули под wasmer.
- Консольные утилиты.
- Библиотеки и модули для других языков (PHP).
- GUI-библиотеку и игру на ней.
- Смарт-контракты для блокчейна (Solana, она сама тоже написана на Rust).
- Макросы и "скрипты" сборки для автоматизации процесса разработки.
Еще хочу попробовать мобильную разработку на Rust, но пока с этим есть некоторые трудности.
Спасибо большое
Есть пример веб-проекта, у которого бэкенд полностью был бы сделан на Rust?
Open-source bootstrap?
Может ли Rust быть заменой языкам высокого уровня?
Есть ли там стандартные обвязки типа ORM, логирование, сбор метрик в прометеус, работа с Redis, авторизация и т.п.
Несколько полна стандартная библиотека?
Прочесть файл, читать данные из TCP-запроса, держать HTTP-соединение и т.п.
Есть пример веб-проекта, у которого бэкенд полностью был бы сделан на Rust?
Ну чтобы далеко не ходить — вот вам пример веб-приложения, бэкенд которого на Rust: https://utex.io/exchange-pro
Для подобных задач Rust показывает себя хорошей заменой C++ и Java/Kotlin. Вообще, я бы предпочел Rust любому высокоуровнему языку из мейнстримных сегодня. Но из-за незрелости в некоторых нишах его использование все еще затруднено (как, например, в мобильной разработке). Хотя именно с точки зрения языка нет принципиальных проблем для его распространения в эти ниши.
ORM в Rust отличается от того, как он реализуется в мейнстримных ООП-языках. Более простой и с растовой спецификой (многие вещи из рантайма выносятся на стадию компиляции). Самая близкая и зрелая реализация — это Diesel. Он может почти все, только плоховато документирован. Хотя для большого количества случаев достаточно sqlx.
Логирование — есть куча неплохих библиотек, полный список здесь. Сбор метрик в прометеус, работа с Redis и RocksDB и т.д. — все это есть, чаще в виде безопасных оберток над Сишными либами.
Стандартная библиотека очень хорошая, но она довольно минималистичная. Работа с сетью, с файловой системой — это, конечно, есть, но, например, полноценного веб-сервера вы в ней не найдете. Для этого есть отдельные крейты, такие как hyper, actix-web, tokio и так далее. Даже фасад для логирования, функционал сериализации/десериализации и работа с регулярными выражениями вынесены в отдельные крейты, которые становятся стандартами де-факто для использования: log, serde, regex и прочие. Вот список всех крейтов по количеству загрузок за все время.
Можете ли рассказать подробнее про использование Rust в utex.io?
Меня интересует архитектура вида
* API Gateway
* Kafka
* воркеры с бизнес-логикой (слушают стримы Kafka, и туда же пишут)
Не нашёл гейтвея на Rust, но туда вполне можно поставить опенсорс на Golang
А вот воркеры на первый взгляд как раз без проблем можно писать на Rust
Именно cargo делает ту работу, которая сейчас унифицировано не делается в C/C++:
1) центральный репозиторий модулей (crate), а не отдельно каждый модуль из интернета
2) сборка проекта с зависимостями (здесь очень важное, что все другие проекты тоже собираются тем же способом, а не разными make, cmake, qmake и т.п.)
3) встроенные: модульное тестирование, форматирование кода и статический анализатор
Почему вы должны попробовать Rust