LINUX.ORG.RU

C++ vs Rust: правда ли, что Rust тупо сложнее крестов в базовых сценариях применения?

 


0

7

Если C++ разраба заставить писать код на определённом подмножестве C++ (в первом приближении: не выделять память руками, не юзать указатели, не кастовать типы), то в принципе течь и падать там будет негде. На деле чуть тоньше и проще: указатели можно, но если тебе его передали в конструктор. Есть циклические ссылки, но тоже можно чё-то придумать. В общем, чёткого набора рецептов нет, опытный разраб в конкретном проекте выработает свои достаточно рабочие, плюс статический анализ и прочие там sanity-check тулзы и valgrind-ы скажут где насрано автоматически.

А есть просто Rust, где достаточно запретить писать unsafe и всё будет гарантированно блестяще и даже думать не надо.

Но говорят, Rust сложнее. Думать там надо уже просто чтобы базово взлететь, тогда как для базового взлёта на C++ достаточно быть тупорылым сишником, которому запретили выделять память. Гонят?

! ! ! ААААА ПРОСЬБА ПЕРЕНЕСТИ В TALKS ОШИБСЯ ФОРУМОМ ! ! !

Дополнение к уже написанному в треде.

  1. Тяжёлое наследие плюсов: его нет, если его не юзать. Я же не пишу в Rust ассемблерные вставки везде. Возможность их написать же не говорит о том, что у Rust тяжёлое наследие всей x86/ARM аппаратной платформы. Нормальный C++ код не содержит никаких макросов, например и передач указателей в пределе. Передай ты std::span, std::string_view и т.п. вместо (char* ptr, uint32_t size).

  2. Никогда не понимал тезис, что синтаксис обычного Си - сложный. Он может быть сложный во всех ВОЗМОЖНОСТЯХ, но в базовых сценариях он кажется примитивным: int function_name(int a, int b) { return a * b;} - это же предельно тупейшая идея синтаксиса, которую придумает любой школьник при наличии задачи изобретения ЯП. Даже конфиги хочется писать в таком стиле - см nginx. Так вот, если сознательно не усложнять себе жизнь, то C++ так же прост.

  3. Посмотрим на такие конструкции Rust, выдернутые из контекста:

languages.get_statistics(&input, &cli.ignored_directories(), &config);

Имеются какие-то &. Не знаю что это, но почему не написано input вместо &input? То есть, юзера заставляют думать про разные виды передачи аргумнетов чтоли? Ссылочно/указательно? Чем это отличается от необходимости в крестах думать про rvalue, lvalue, reference, pointer? То есть, от этого момента язык тоже не ушёл: нельзя как в JS/Python херануть объект в аргумент и зашибись - надо думать как херануть.

(0..10).map(|_| "#").collect::<String>()

Питонячно. Какой-то генератор с вызовом какой-то лямбды на каждый объект генератора? Не в курсе как это точно работает, но питонячно! В современных плюсах подобное тоже выразимо, но это уже всё равно не уровень начинающего: понимание подобного что в плюсах, что в расте - признак не дебила.

fn main() -> Result<(), Box<dyn Error>> {

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

let mut is_sorted = false;

В Rust это выглядит НАДЁЖНЕЕ чем в С++, потому что заставили написать mut, чтобы сообщить, что это можно менять. В C++ это выглядит так же коротко в принципе: auto is_sorted = false;. Но в крестах ты пишешь const auto is_sorted = false; если надо конст и всё.

Посмотрим как пилятся сруктуры в расте:

pub struct CodeStats {
    /// The blank lines in the blob.
    pub blanks: usize,
    /// The lines of code in the blob.
    pub code: usize,
    /// The lines of comments in the blob.
    pub comments: usize,

Блин, в C++ же проще:

struct CodeStats {
  // The blank lines in the blob.
  usize blanks;
  // The lines of code in the blob.
  usize code;
  // The lines of comments in the blob.
  usize comments;

Я потратил меньше кода в крестах. Мне не надо писать pub напротив каждого поля, я могу его вынести в начало. Плюс, struct в крестах - это по-дефолту всё pub, а class - по-дефолту всё private - можно регулировать приватовость всех полей сразу просто выбором слова, которым объявлять структуру.

Что бесит в Rust: тип в конце. Но в языках, где важна производительность, люди любят подумать про memory layout - «как всё лежит в памяти» и посмотреть в первую очередь на типы всего, что лежит в структуре: какой тип рядом с каким, как это выровняется, например. Понятно, что в структуру в таких случаях данные пихают не по выравниванию, а «что рядом с чем потребляется процом», чтобы «нужное вместе» в одну кеш-линию, поэтому важнее будут имена полей, чем типы. Но всё-таки хочется «от общего к частному»: сначала видеть ЧТО ЭТО В ПРИНЦИПЕ (какой у этого тип), а уже потом как оно называется. Условно, мне хочется в «семантике общения» ситуацию «это собака, её зовут Вася и это тоже собака, и гоша», а не наоборот: «это Вася, а ещё он собака, а это петя и он собака». Я не хочу думать про имена, я хочу сначала схватывать суть уровня «так, тут у нас две псины, что они тут делают», а как они называются я потом разберусь)

pub fn summarise(&self) -> Self {

Об этом уже говорилось, в C++ тут будет меньше кода. А где тип аргумента, статически типизированные вы наши, йопт? В целом понятно, почему они заставили писать pub перед каждой функцией - чтобы тупорылый разраб сразу видел точно публичное оно или нет. А то в C++ напишут слово public: а дальше ряд функций и могут случайно написать функцию не в той секции и она случайно будет public – лучше пусть явно пишут! Но хз, это вкусовщина: мне приятнее организовывать всё именно как в плюсах: написать public и дальше у нас красиво пошёл публичный интерфейс. Нафиг мне pub в глаза пихать на каждый чих. Та же конструкция в C++: Self summarise(T &self) {. Опять же, уже говорилось: наличие & - раст оказался не таким уж простым, юзеру надо думать ссылка там или не ссылка? Где такая же простота, как в ссаном JS, что просто self написал и всё?

Давайте просто сюда посмотрим: https://github.com/sharkdp/fd/blob/master/src/filesystem.rs – в принципе да, всё читаемо, красиво, выразительно. map всякие там. Отсутствие скобок у if бесит конечно, ну ладно, в питонячке так же. Но в целом код на современных крестах выглядит абсолютно так же, может чуть меньше символов напечатать придётся. А где-то вместо map().blabla().bubu() будет несколько процедурных строк, потому что в именно стандартную библиотеку C++ не подвезли именно такой семантики, но она достижима в самом языке.

Посмотрим сюда: https://github.com/XAMPPRocky/tokei/blob/master/src/input.rs

Спецсимвольный perl-адок какой-то немного. Зачем так жить. Степень жести в районе строк 16-26 вообще ничем не способна привлечь юзера в сравнении с самыми мерзкими местами C++. Ясно, что это всё можно как-то объяснить. Точно так же «как-то» можно объяснить вот такой C++ код: https://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/src/c++11/thread.cc#L235 - синтаксис обычный такой «сишный», просто вперемешку с макросами и большим количеством ___ в названиях переменных: в общем, оба фаната «как-то» объяснят ад в своих языках, простой мане, которая хотела «простой язык» оба этих места одинаково жопные.

Хочется обратить внимание на такой тонкий психо-нюанс, который похоже имеет место быть в расте: раст далеко не питонячка, всмысле в нём уже таки надо думать о неком таком критическом количестве вещей, что требования к мозгу кандидата поднимаются на такой уровень, где ему уже совсем не противен C++ и возможные дедлоки в тредах не кажутся чем-то сложным: ой, опять мьютексы не в том порядке захватил, поправил и забыл.



Последнее исправление: lesopilorama (всего исправлений: 4)

Ответ на: комментарий от eao197

можно простыми структурами пользоваться.

нет нельзя:

auto cing =  Disconnected{}.connect();
auto ced = cing.established();
ced = cing.established(); // ой, сломалось, по протоколу такое можно только 1 раз проделать
ced.successed_ping().drop();

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

Ну и, конечно же, отдельные ветки для обработки успешного и неудачного пингов – это доставляет.

А что должно быть? не отдельные ветки - это как? никакой компилтайм в будущее не может заглянуть и будет разделение в рантайме и именно 2 не 3 не 1.5, это суть выбора из булевского значения(и дело не в смысле термина пинг)

zurg
()
Для того чтобы оставить комментарий войдите или зарегистрируйтесь.