Pull to refresh
30
0
Станислав Ткач @DarkEld3r

Rust developer

Send message

По моему опыту, опыт в других языках, особенно С++, часто идёт в зачёт.

Они отвечают на этот вопрос вот так:


Existing modern languages already provide an excellent developer experience: Go,
Swift, Kotlin, Rust, and many more. Developers that can use one of these
existing languages should.
Unfortunately, the designs of these languages
present significant barriers to adoption and migration from C++. These barriers
range from changes in the idiomatic design of software to performance overhead.

Carbon is fundamentally a successor language approach, rather than an
attempt to incrementally evolve C++. It is designed around interoperability with
C++ as well as large-scale adoption and migration for existing C++ codebases and
developers.

Эта библиотека упоминается в статье.

Вы, вероятнее всего, считаете, что питон — это детский сад, и все благородные доны должны писать на coq (haskell, whatever)

Вангую, что Siemargl признаёт только С++.

То есть, если unsafe спрятать внутрь, то как бы и не считается?

Никого ведь не беспокоит, что где-то внутри условной JVM есть нативный код, который всякого натворить может?.. На джаве пишут принимая как факт, что сборка мусора работает корректно и всяких сишных UB не случится.


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

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

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

Это тоже спорно. (:


Ограничения раста проще понять вдоволь потоптавшись по граблям С или С++. Иначе может быть непонятно ради чего это всё.

Слышал, что Idris "исправляет" какие-то моменты в хаскеле, но он ещё и минималистичный и расширяемый?.. А есть шансы, что он начнёт теснить хаскель? Или преимущества не настолько большие, чтобы перевесить отсутствие библиотек и инфраструктуры?


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

Пишу на крестах в основном, хоть убейте, не понимаю и не вижу преимущества Rust.

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


Преимущества Rust я бы разделил на несколько (размытых) категорий:


  • Инфраструктура. Тут я ничего нового не скажу — карго хвалят все подряд. Могу разве что обратиться к плюсовому опыту — в своё время сильно радовался переходу на CMake, но на нескольких последних проектах поверх CMake было написано куча своего кода. В итоге каждый раз приходилось разбираться заново. Казалось бы, небольшая проблема, но теперь я вижу, что можно лучше: просто иметь декларативный конфиг для подавляющего большинства случаев. Тут обычно возражают, что каждый плюсовый проект сильно особенный. Не согласен, но раст вполне себе позволяет для особых случаев писать билд-скрипты. Причём на самом расте, а не на отдельном языке.


  • Правильные умолчания. Это когда при доступе к элементу массива/вектора у нас будет паника, а не UB, если мы вышли за границы. При этом есть get_unchecked альтернативы позволяющие избежать лишних проверок в горячем коде, если оно действительно нужно. При этом данная функция и unsafe и писать длиннее, так что просто так ты это делать не будешь. Сразу хочется комментарий написать почему оно тут нужно, иначе на ревью всё равно спросят. Да, я понимаю, что в С++ для новых сущностей просто поступали последовательно, но от этого не легче.


  • unsafe. Отчасти пересекается с предыдущим пунктом, но хочется выделить отдельно. Собственно, частый аргумент, что на расте ничего без ансейфа не написать. У меня несколько (если не сказать совершенно) другой опыт. Зачастую даже написание новой библиотеки начинается с ![deny(unsafe_code)] в корне. И очень часто оно так и остаётся. В любом случае, цель не избегать "небезопасного" кода любой ценой, цель — не размазывать его по всей кодовой базе, а завернуть в безопасную абстракцию. А явный маркер весьма помогает при обзоре кода.


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


  • Макросы, особенно процедурные (и derive). Опять же, в плане написания это далеко не верх удобства, но пользоваться готовым весьма приятно. Для сериализации в С++ пользовался cereal, так вот просто добавить структуре атрибуты Serialize/Deserialize намного проще. Или можно сравнить clap с монструозным boost::program_option.


  • Это просто современный язык со всякими штуками вроде паттерн матчинга, нормальных тип-сумм, "всё выражение", константности по умолчанию, простой семантики перемещения и т.д.



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

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


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


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

А можно несколько примеров этих альтернатив?

Так вот в Rust — компиляторная магия. Они говорят, мол у них макросы, на которых можно сделать что угодно. Но зарывшись в дебри поглубже видишь там "/ compiler builtin/". Из чего понимаешь, что сам так — не сделаешь.

Если говорить конкретно о println!, то в виде "compiler builtin" оно реализовано потому, что к версии 1.0 макросы ещё не были стабилизированы. Сейчас аналог можно написать самому.

Справедливости ради, ИДЕ тоже решают эту задачу: IntelliJ Rust и последнюю версию показывает и автодополняет по названию. Правда не проверял умеет ли так rust-analyzer.

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


Комбинированное внесение в область видимости заставляет спотыкаться:

А мне наоборот нравится, что сразу видно где тип, где функция, где переменная, а где константа.


To/From, Into_ типы, трейты и вспомогательные функции тоже не самодостаточно именнованы, чтобы понимать о чем речь. Какое правило по превращение одного в другое: B = A.into() или B = B.from(A), и прочие противоречивости.

Вот это не очень понял. Почему From и Into именованы не самодостаточно, если всё что они делают — это просто преобразуют из одного типа в другой. Как-то даже затрудняюсь придумать как название можно расширить чтобы сделать "более очевидным".


Зачем два? Потому что много где не надо явно указывать целевой тип — он будет известен из контекста и достаточно просто а.into(), а там где нужно указать тип — там удобнее X::from. В доке, вроде как, вполне внятно это описывается, а так же какой из трейтов надо реализовывать чтобы автоматически получить реализацию второго.

Для развития педагогических навыков — какие символы тут непонятны?

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

println!("{:?}", my_variable);

Можно вот так: println!("{my_variable:?}");


Вам надо будет добавить их вручную (вместо использования такой команды, как yarn add)

Можно использовать cargo add (установив cargo-edit).

Если труд не оплачивается, это не профессиональная деятельность, а хобби

Немало опенсорса пишется за деньги.


А значит СПО всегда будет проигрывать в скорости разработки, функционале и безопасности ППО.

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


Поэтому в серьезном ПО использовать СПО по меньшей мере безответственно.

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

Если вы строку оборачиваете в Path как это решает проблему?

Возможно, я плохо объяснил, попробую ещё раз. У функции такой контракт — она принимает путь:


fn foo(p: &Path) { ... }

Если я, как пользователь библиотеки, хочу "захардкодить" путь, то и хотелось бы просто написать foo("some/path"), а не foo(&Path::new("some/path")). Можно, конечно, изменить функцию — принимать строку, немного пожертвовав наглядностью, а то, что это путь — написать в документации. Но тогда если у меня как раз есть Path или даже скорее PathBuf, который получен после каких-то манипуляций, то мне уже будет менее удобно пользоваться новой функцией. И тут на помощь приходят дженерики (с трейтами): AsRef<Path> — это как раз способ сказать "функция принимает любой тип, который преобразовывается к пути" (смысл у AsRef чуть-чуть другой, но сейчас это не важно). И эта функция будет автоматически работать и со строками и со строковыми слайсами и OsStrOsString) и т.д.


В библиотеках что я видел как раз код пронизан Option/Result и д.р. монадами, почти в каждой функции есть паттерн матчинг.

Может, конечно, у меня глаз замылился. Хотя до раста продолжительное время писал на С++, где как раз чаще используются исключения. Тем не менее, принятому в расте подходу я очень рад.


Используя try/catch код каждой функции можно сократить.

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


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

Не могу согласиться.

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

Это можно сказать и про макросы (и в расте и вообще) и про шаблоны в С++ и практически про любой языковый механизм. Ту же перегрузку операторов можно интересно использовать давая новый смысл привычным операциям. А уж если можно свои новые операторы вводить, то и тем более. Сложившиеся в языке/инфраструктуре практики действительно влияют на "средний" код, но я бы не сказал, что в расте ситуация сильно выделяется. Особенно если сравнивать со схожими языками: С++ или той же скалой, а не с С или Go. По моим ощущениям, макросами (которые читать куда сложнее, чем обобщённый код с трейтами) стараются без необходимости не увлекаться.


Эту проблему вижу в каждом проекте.

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


не видел проблем при преминении концепции try/catch, код работает стабильно на всех языка что писал

Не очень понимаю, что это значит. Код будет работать так как написан. Но писать с исключениями действительно проще и быстрее. Отлаживаться — дольше. Если, в первую очередь, критична скорость реализации, код пишется рассчитывая на на "happy path", а try/catch втыкается уже по результатам тестирования, то да раст — не лучший выбор.

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

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


Возьмём самый банальный пример: функция, которая принимает путь. Мы можем принимать Path, PathBuf, строку и т.д. Если выберем что-то конкретное, то код будет проще, но пользоваться им будет менее удобно. Приходится делать функцию обобщённой (AsRef<Path>). Код стал (немного) сложнее, зато пользоваться им проще. Как по мне, так это вполне себе выигрыш. Как сделать лучше, если выкинуть "бомбу" в виде трейтов?


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


Казалось бы Option/Result благое намерение но большинство кода, что я видел, выглядит как бесконечное unwrap()

Можно несколько примеров из популярных библиотек? Так чтобы были именно "бесконечные анврапы".

Information

Rating
5,078-th
Date of birth
Registered
Activity