Ads
Comments 234

Проделал путь java — > c — > python — > rust, и теперь даже скрипты для аналитики пишу на нем. Ибо в программе на питоне не вылезаешь из дебагера, в расте дебагер я не использовал не разу. Как пример, был многоуровневый json, нужно было его его сгрупировать различными способами и вывести по этой группировке статистику.
В расте это представимо как HashMap<str, StatsStruct >, в питоне же это HashMap<str, Hasmap >.
На питоне скрипт написал быстро.
Потом через неделю структура данных немного изменилась и рефакторинг превратился в ад по понятным причинам(ручной поиск и переименование ключей для мап и отсутствие какой либо помощи от интерператора).

Прикладной, говорите. А как будет выглядеть на Расте такой код 10-летней давности ?
var
  i, j: Integer;
  Sheets: Variant;
begin
  XLApplication := CreateOleObject('Excel.Application');
  XLApplication.Visible := True;
  XLApplication.Workbooks.Add;
  XLApplication.Workbooks.Add(xlWBatChart);
  XLApplication.Workbooks.Add(xlWBatWorkSheet);
  XLApplication.Workbooks[2].Sheets.Add(,,1,xlChart);
  XLApplication.Workbooks[3].Sheets.Add(,,1,xlWorkSheet);
  for i := 1 to XLApplication.Workbooks.Count do begin
    ListBox1.Items.Add('Workbook: ' + XLApplication.Workbooks[i].Name);
    for j := 1 to XLApplication.Workbooks[i].Sheets.Count do
      ListBox1.Items.Add('  Sheet: ' +  
        XLApplication.Workbooks[i].Sheets[j].Name);
  end;
end;

Ну, например, так:


use excel::{Application, WorkbookTemplate, SheetType};

let mut app = Application::new()
    .with_visible(true)
    .with_workbooks([
        WorkbookTemplate::Empty,
        WorkbookTemplate::BatChart,
        WorkbookTemplate::BatWorkSheet,
    ].into());
app.workbooks[1].sheets.add(SheetType::new_chart().with_count(1));
app.workbooks[2].sheets.add(SheetType::new_work_sheet().with_count(1));

for workbook in &app.workbooks {
    list_box.items.add(format!("Workbook: {}", workbook.name));
    for sheet in &workbook.sheets {
        list_box.items.add(format!("  Sheet: {}", sheet.name));
    }
}

Мы обсуждаем возможности самого языка или имеющиеся библиотеки? Я вам показал, как выглядел бы код на Rust, который привели вы. Есть ли в экосистеме Раста библиотеки для работы с excel — это другой вопрос, к статье отношения не имеющий.

В примере демонстрируется не какая-то библиотека, а динамические возможности Delphi при интеграции ActiveX компонента. Т.е. там дельфи не знает про excel, а знает только про IDispatch.

И где там эти динамические возможности демонстрируются? Я вижу статическую строку 'Excel.Application', вижу статические обращения типа XLApplication.Workbooks — это все написано в самом коде программы. Где динамика-то?

Тем что никак не надо описывать нигде отдельно интерфейс Excel компонентов. И этот код можно скомпилировать на машине без установленного Excel.


Я не говорю, что этот подход во всем лучше чем Rust. Возможно, где-то есть библиотека, которая содержит макросы, которые реализуют type provider для библиотек типов COM, но это по своим свойствам будет отличаться от динамики — надо будет извлекать типы из excel, где-то их сохранять и так далее.

А отсутствие проверок типов — это точно достоинство? Компиляция на машине без установленного Excel может быть достигнута куда меньшей кровью — надо лишь взять TLB от Excel и сохранить его куда-нибудь. Далее можно по этой TLB генерировать код уже на любом компьютере (тут слово "можно" означает отсутствие фундаментального запрета, существование генераторов прямо сейчас я не проверял). И компилироваться сгенерированный код тоже будет на любом компьютере.

Отсутствие проверок типов это и достоинство и недостаток. Как и почти что угодно.


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


существование генераторов прямо сейчас я не проверял

Для прикладного программиста существование генераторов прямо сейчас может быть гораздо важнее, чем какие-то принципиальные возможности.


Например, представьте что вам прямо сейчас надо сделать простой отчет на Excel, сохранить его в PDF и отослать кому-то. Решение будет работать ровно в одной конторе для одного пользователя — бухгалтера Марии Васильевны, что вы выберете?


Интересно, что в интервью Андрея Бреслава, автора, Котлин "королю программирования" первый сказал, что какие-то возможности динамической типизации есть везде, причем в собственном стартапе они только сейчас доросли до того, чтобы начать применять typescript — cейчас все на JS.

Представьте, что вам прямо сейчас нужно сделать утилиту командной строки, которая должна работать в Linux-дистрибутиве, для прикладных целей. Что вы выберете? Delphi? А если не его, то значит ли это, что Delphi — не прикладной язык программирования?

Не совсем относится к вопросу, но все же.


Лично я бы выбрал python запакованный pyoxidizer. Грубо говоря интерпретатор Python в Rust приложение. Это улучшает переносимость и если утилитка выстрелит то постепенно бы переносил части на Rust. Но это не точно, ещё не освоил на должном уровне.

В новых Дельфях уже завезли Linux, даже гуёвый, так что пример не такой уж бьющий в цель))
Можно и на Дельфи, но есть для этого более удобные языки — Питон, ПХП. Да и на Го и Д это будет делать удобнее, чем на Расте.

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

А еще это вранье вдвойне — потому что модель память OLE не натягивается на владение Rust (просто так).
язык без библиотек — это не язык прикладного программирования — и не надо вводить людей в заблуждение

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


А еще это вранье вдвойне — потому что модель память OLE не натягивается на владение Rust (просто так).

А вам не все равно, OLE-объектом вы оперируете в программе или нет, если результат "натягивается" на то, что вам нужно помимо вашего непосредственного участия? Object Pascal, считайте, повезло, что он имеет такое же представление vtable, как в C++. И что, мы теперь все языки с моделью памяти, отличной от C++, вычеркнем из разряда прикладных?

Вот вам пример из библиотеки intercom, как можно сделать API в Rust для работы с COM-объектами (они тоже основаны на vtable):


Rust COM server:


pub use intercom::*;

#[com_library(Calculator)]

#[com_class(Calculator)]
struct Calculator {
    value: i32
}

#[com_interface]
impl Calculator {
    pub fn new() -> Self {
        Self { value: 0 }
    }

    pub fn add(&mut self, value: i32) -> ComResult<i32> {
        self.value += value;
        Ok(self.value)
    }
}

C# COM client:


class Program
{
    [STAThread]
    static void Main( string[] args )
    {
        var calc = new Calculator.Interop.Calculator();
        calc.Add( 10 );
        var result = calc.Add( 100 );
        Console.WriteLine( result );
    }
}

Вся магия с указателями и прочим скрыта за атрибутными процедурными макросами.

А, наоборот — клиента на Раст, который работает с чем-то через COM? Поддержка использования библиотек типов и так далее?

Теоретически реально, крейт winapi достаточно хорош и стабилен. Другое дело, что все это обмазано unsafe, как цепь дедового велосипеда солидолом, что может смущать.

По факту никто этим заниматься не будет — сам по себе winapi интересен только низкоуровенщикам. Максимум — гуеписцам.

Я вам не могу написать реально работающего примера, так как программированием под Windows не занимаюсь. Из примера выше должна быть понятна общая идея: в Rust есть низкоуровневые возможности, а значит в принципе реализовать можно работу со всем, чем угодно, при этом можно сделать удобный и безопасный высокоуровневый API.


На вскидку, я думаю, что внешний API может быть представлен в виде атрибута, который вешается на статически определенную структуру. А если вам требуется динамика и на момент компиляции вы точно не знаете структуру данных — то дополнительно можно сделать API для работы с такими данными на основе АТД. Так сделано в библиотеке serde_json: вы можете распарсить JSON из строки в АТД Value, либо в собственную структуру, если формат JSON-объекта известен заранее.


Обратите внимание, что в исходном примере на Delphi динамика скорее мешает — она никак по-сути не используется, все записано статически, а контроля типов при этом нет.

Обратите внимание, что в исходном примере на Delphi динамика скорее мешает

По сравнению с гипотетической отсутствующей системой, которая включает генерацию типизированной обертки по TLB, да. Но реально этой системы не существуют, возможно, потому, что ее сложнее делать чем просто вызвать метод IDispatch.Invoke пор строчке. При этом система на основе TLB будет работать только с теми приложениями, которые предоставляют наружу TLB а не всеми для которых работает OLE automation.

Возможно, потому, что Растом пока не пользуются уж очень сильно как совсем прикладным языком?
Для подобных задач достоинства Раста менее важны чемегонедостатки.

А может стоит сделать по другому? — написать новую статью в каких областях Раст силен, и чем именно, вместо общих фраз и лозунгов.
Навскидку area, для примера
  • embedded/iot
  • app's
  • web server
  • web client(wasm)
  • coresys/libs
  • scripting
  • gamedev
  • math/gpu/distributed/AI
Голый язык мало кому нужен, конечно экосистема.

Забыл еще область программирования для мобилок — andriod/ios

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


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


Вам это не нравится? Что же, поклонником Rust вас не назвать, скорее — противником. А если ты делаешь что-то, что не нравится твоему противнику, то это значит, что ты поступаешь правильно. Вот о чем мне говорят ваши комментарии.

У меня нет возражений по первым двум абзацам.
Но вот то, что молоток — универсальный инструмент, еще не делает его пригодным для строительства небоскреба. Разночтение в понимании «универсальный ЯП».

По поводу третьего — так что, моя идея показать силу Раста в конкретных применениях отклоняется из желания насолить «противнику»? =)

Кстати, я нашел общее, что мне не нравится в статьях растаманов — упоминаются исключительно только Pro-, но никак не Contra- аргументы. Вот и помогаю составить целостную картину в меру своих ограниченных способностей.
Чего то подобное есть в планах, но раста касается опосредованно -только как одного из многих, и нескоро.

Не вижу смысла писать чисто негатив — это точно также искажает картину. В конце-концов Раст дал живительного пендаля целой индустрии, а это чего то да стоит!

Надо просто сделать State of the Rust ecosystem по аналогии с этим (хотя я по последней ссылке не со всем согласен, например, numerical programming вполне ок, да и machine learning я довольно продуктивно делал, ну да ладно).

Это какой-то новомодный тренд заводить десятки похожих доменов вместо заведения одной wiki? После wiki-статьи «State of the Haskell ecosystem», набор ссылок «areweyet чего-то там» оставляет впечатление какой-то маркетологической фигни.
Кстати, для ссылки arewestableyet даже валидный сертификат не подвезли.

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


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

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

Не новомодный — это старая полустебная традиция из Мозиллы.


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

По личному опыту — то ли TLB, то ли в дельфишном генераторе модулей из TLB встречаются ошибки, из-за которых полностью статически написанное взаимодействие с Excel/Word не всегда работает. И время от времени приходилось писать что-то типа
Variant(workBook). Save(reWrite := xlRewrite). Конкретных функций и параметров уже не помню, но иногда без этого просто не работало.


Зато можно было сгенерить код на VBS (записать макрос) и скопипастить в Delphi, получив рабочий код с минимальными правками (работало это как раз через IUnknown по умолчанию).

Я привел работающий пример. Нужен Эксель, а не Калькулятор.

Если для этого и подобного мне нужно каждый раз писать собственную [системную] библиотеку, этот язык не годится для прикладного программирования. Batteries Not Included

Эксель тут только для примера — для прикладного программирования нужен нормальный фреймворк промышленного качества, поддерживаемый, с сетевыми функциями, драйверами к СУБД, ГУИ, функционально полным Веб — фреймворком. Пока этого нет, говорить рано.

А как вы думаете, сколько усилий было вбухано в эти самые драйвера СУБД, ГУИ, веб-фреймворки на С++? Вы хотите, чтобы это всё резко появилось в языке, в который усилий вложено порядка эдак на три меньше?


Кстати, как там дела с подобным dynamic binding в С++?

Я хочу, что бы не было попыток ложной рекламы.

В С++ тоже весьма неудобно, хотя OLE там родное. Но с помощью MFC (либо С++Builder'a) и какой то матери, жить можно «из коробки». А почему Вы меня спрашиваете про С++?

OLE там родное, потому что оно изначально под C++ и делалось.


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

В С++ тоже весьма неудобно, хотя OLE там родное. Но с помощью MFC (либо С++Builder'a) и какой то матери, жить можно «из коробки».

По сути, это примерно то же, что генерация с помощью процедурных макросов.


А почему Вы меня спрашиваете про С++?

Признаю, тут я ненамеренно переключил контекст. Прошу прощения.


Впрочем, если касаться Delphi/Object Pascal, конкретно эту фичу скорее всего проектировали конкретно под поддержку COM/OLE. С моей точки зрения, отсутствие/наличие какой-то одной подобной фичи не делает язык специализированным/общего назначения. Сможете ли Вы так сделать "из коробки", зависит от того, написал ли кто-то ранее нужную библиотеку. Сможете ли вы так сделать удобно и быстро "из коробки", зависит от того, посчитали ли авторы языка эту фичу достаточно важной для встраивания в ядро.


Как контр-пример — задача работы с 3D Boundary Representation (BRep) данными. Есть ли для Delphi/Object Pascal подобные вещи "из коробки"? Сомневаюсь. Есть ли подобные библиотеки в принципе? Может быть, не знаю. Делает ли это Delphi хуже? Нет. Делает ли это Delphi в принципе неприменимым для задачи? Нет.

Как контр-пример — задача работы с 3D Boundary Representation (BRep) данными. Есть ли для Delphi/Object Pascal подобные вещи «из коробки»? Сомневаюсь. Есть ли подобные библиотеки в принципе? Может быть, не знаю. Делает ли это Delphi хуже? Нет. Делает ли это Delphi в принципе неприменимым для задачи? Нет.
Ну это уже совсем редкий случай (в отличие от Экселя), я бы взял для примера какие нибудь мат.библиотеки.
И мой ответ был бы таким — Дельфи тут не применяем, для этой задачи берем язык с адекватным набором либ.

А вот для меня как раз пример с Экселем видится мелким и редким. Потому что мои программы должны уметь формировать документы MS Office даже если на компьютере не установлен этот самый MS Office и к тому же нет активного сеанса пользователя.

Ну это уже совсем редкий случай (в отличие от Экселя)

Ну как вам сказать… AutoCAD, SolidWorks, Siemens NX, CATIA, Creo… Это первые пришедшие в голову крупные CAD/CAE пакеты. Я с экселем за последние лет 12 столкнулся один раз, с его COM API — ни разу. А с CAD'ами последние 5 лет имею дело. Так что тут забавный вопрос, какая область применения "ширше".


И мой ответ был бы таким — Дельфи тут не применяем, для этой задачи берем язык с адекватным набором либ.

Значит дельфи тоже не прикладной? Или всё-таки прикладной?

Надо разделять OLE Automation и работу с форматами данных. Пока что обсуждалось первое, и проблема не поменяется от замены слова Эксель на Автокад.

Значит дельфи тоже не прикладной? Или всё-таки прикладной?
К другой области прикладной =)
Если для этого и подобного мне нужно каждый раз писать собственную [системную] библиотеку, этот язык не годится для прикладного программирования.

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

Я пока только лишь изучаю Rust, но во-первых, для Rust есть и драйвера к СУБД, и ГУИ и даже веб-фреймворк (наверняка даже не один). Кроме того, в нем также можно задействовать существующие C и C++ библиотеки (придется делать дополнительные движения, конечно, но многие популярные уже обернуты и доступны на crates.io).


Кроме того, я пишу уже более 20 лет на Python (где-то года с 1999). Сейчас не осталось наверное почти ни одной библиотеки или фреймворка, из тех, что я использовал в 1999. Те же драйвера для СУБД для Python переписывались неоднократно, некоторые с нуля, притом. То же самое справедливо почти для любого другого языка.


В общем, в силу того, что я пока только прочитал ⅔ Rust Book, и еще не написал ни одного полноценного приложения на Rust, не могу полноценно сравнивать его ни с чем, но пока что он мне нравится.


До Python я еще делал попытки писать на C++, и это было сложно, на мой взгляд. А сейчас, когда смотрю на современный C++ — ну это просто какой-то дикий комбайн, для управления которым надо получить PhD и еще 10 лет опыт нарабатывать.


Rust в сравнении нравится тем, что в нём все просто и логично. C++ при всем желании простым и логичным не назовёшь.

В общем, в силу того, что я пока только прочитал ⅔ Rust Book, и еще не написал ни одного полноценного приложения на Rust, не могу полноценно сравнивать его ни с чем, но пока что он мне нравится.
Прекрасно — буду рад увидеть от Вас статью типа «как я переписывал свое приложение с Питона на Раст».

Когда я вижу библиотеку версии — 0.3, например для MSSQL, я сомневаюсь тащить это в прод.
Когда я вижу библиотеку версии — 0.3, например для MSSQL, я сомневаюсь тащить это в прод.

Про zerover слышали? Говорят, сейчас модно, а в Rust особенно. :)
https://0ver.org/

ОМГ, молодежно, буду знать. И РеактОС в этом списке яркий пример небеты!
Еще неизвестно, что хуже — 0.3 или 152.3.2 как форсит Хромиум, за которым подтягивается и Мозилла. Когда три внутренних багфикса наращивают мажорную версию — это, на мой взгляд, перебор.

Именно этот — дословно так же. Даже чище, поскольку не нужно объявлять все вспомогательные переменные сразу и пользоваться for-циклом для итерации.


Упд: собственно, ответ топикстартера выше демонстрирует.


Отдельное спасибо НЛО за редактирование в мобильной версии сайта!

Как я понял, библиотека специализированная на работе с xlsx-файлами без экзеля? Тогда это совсем не то. Код на Delphi может также показать файлик пользователю, обработав при этом попытку закрытия файла, потом, не закрывая excel-я, считать всё, что пользователь там понавводил.
А по ссылке — просто библиотека для создания/редактирования одного формата. Для какого-нибудь веб-сервиса, которому надо отчетик сгенерить без графиков — это то, что нужно, но для приложения, которому нужны все возможности excel-я этого будет мало.


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

А что у вас за проблемы с растом, господин симаргл?
Вы приходите в каждый тред про раст, пишите довольно странные вещи.
Вам каждый раз терпеливо аргументируют, вас минусуют, по существу. Но вы продолжаете по новой.
Без наезда, просто интересно, что заставляет человека так воинствующе пытаться очернить то, в чем вы не шарите.
Тут ведь явно какие-то личные мотивы.
Не люблю ложную и навязчивую рекламу.
В данном топике это, что раст подходит всем.
Ну вы искажаете суть. Как древняя шутка про джаву:
«Saying that Java is nice because it works on all OS's is like saying that anal sex is nice because it works on all genders»

Не сказано что он подходит всем. А подходит для всех задач.
Вы используете не хорошие приемы, для отстаивания своей точки зрения.
Отвечаете на не существующие тезисы. Ведете всякие демагогические приемы.

Когда такое делает Полухин это понятно. Для него это job security, и удовлетворение потребности в признании. Он признаваем в мире плюсов и боится, что его этого лишат.
Но вам то какая разница? А если разница и есть, то аргументы нужны хотя бы по существу.
У Антона они кончились и он замолк, а вы то зачем продолжаете?
Вы используете не хорошие приемы, для отстаивания своей точки зрения.
Отвечаете на не существующие тезисы. Ведете всякие демагогические приемы.
Конкретнее, пжлста.
Я привел пример прикладной программы, умеющей в OLE Automation, это демагогия?
Ну тогда это просто грязный пиар — язык без библиотек — это не язык прикладного программирования — и не надо вводить людей в заблуждение.


Вы всегда, на любом языке/инфраструктуре, сможете найти либу, которой нет для какой-то вашей экзотической задачи.
Что это доказывает? Это и есть самое настоящее искажение. Либы пишут компании за деньги. Если вы считаете, что для чего-то нет апи на таком-то языке, то это не язык вас обманул, а человек, который не предоставил апи, ибо, умер\нет времени\нет денег\ нет желания.

Ну и потом. Здесь речь не про конкретно эту ветку. Я смотрю по вашим комментариям, они же все такие. Вы врываетесь в каждый тред, пишете какую-то дичь. Вам не смотря на это отвечают по существу и аргументировано. У вас конечно аргументов не остается и вы идете «гадить» дальше. Такое возможно только за зарплату или по личной глубокой обиде/страху.

Вы иногда задаете и правильные вопросы. Но даже в таком случае, тон все-равно остается очень «с наездом».
Ну без конкретики на эмоции я отвечать не буду. Где то я бываю неправ — тогда я это признаю.

Этот человек — из "группы поддержки" языка D, как я понял. Так что да, Rust для D все-таки конкурент и у D дела пока идут хуже, чем у Rust.

Отличная стратегия поддерживать свой любимый язык, пытаясь обливать помоями другой язык. Типичный подход неудачников.
Лучше бы сделал что-то стоящее на D, раз такая любовь.
Но как известно, трындеть — не мешки ворочать.
Критическая статья про D в черновике.

Какие то любители крайностей, право слово.

Только не очень понятно про какой язык вы в первом предложении.

Частенько вижу новости по расту и уже несколько лет пытаюсь понять почему он существует.
Признаюсь, я не пытался его изучать как раз по этой же причине (потому что мне дорого мое время и я могу его потратить на экосистему в которой я уверен).
Насколько я понимаю мозилла предлагает его как безопасная замена C++, но синтаксис (и это не только я заметил) крайне отвратный (в том числе в этой статье), даже по сравнению с плюсами. Судя по докладу от уважаемого мной, но предвзятого человека (Антон Полухин известный адепт C++), www.youtube.com/watch?v=fT3OALUyuhs каких-то новшеств в безопасности от rust'а ждать не стоит. Кроме borrow checker'а, который судя по докладу не работает и непонятно когда заработает. «Safe mode» в rust как я понимаю просто является встроенным в язык статическим анализатором кода не особо отличающийся(?) от таких же анализаторов на с++. Поэтому я совершенно не понимаю зачем он вообще нужен и почему бы не улучшать тот же с++, вместо того чтобы плодить еще одну экосистему со своими проблемами.
Может я чего-то не понимаю и пропускаю что-то важное? Буду рад если вы меня переубедите.
Во-первых прочитайте статью (и комментарии к ней) от humbug — станет яснее.

Borrow checker в Rust отлично работает, зря вы так.

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

Это действительно хороший вопрос. Многие считают, что С++ уже не исправить. Лично я так не считаю и своим развитием С++ показывает, что он умеет эволюционировать. Смысл в том, что C++ сейчас как раз и движется в сторону многих вещей, которые Rust уже предлагает. Кому не хочется ждать и сразу писать на готовом (но при этом без кучи библиотек, наработанных за всё время на С++) — могут использовать Rust. Для тех, кто хочет использовать уже готовые решения — используйте С++ и дальше, обмазывая его, как вы правильно заметили, уже практически идущим в комплекте статическим анализом, пакетниками и так далее и будет вам счастье.

Какой путь больше подходит — решать Вам.
Проблема в том, что C++ ушёл настолько далеко вперёд, что у новичков уже нет шансов его полноценно освоить. Да, лет за пять можно более-менее загрузить в мозг текущее состояние языка и связанной с ним экосистемы. Но это будет лишь текущий статический срез, который не даст представление о best practices и общепринятых способах решения проблем.
По-настоящему язык знают лишь те, кто развивался вместе с ним. Кто изучал C++ в динамике, по мере его формирования — читая рассылки и форумные дискуссии, и даже участвуя в них. Сталкиваясь с проблемами — и наблюдая, как в стандарт и в экосистему приходят новые способы их решения. Только тогда человек точно знает: как, почему и для чего появилась та или иная возможность или особенность стандарта.
Ни одна книга не даст этого опыта. Этот поезд уже ушёл, и максимум, на что можно рассчитывать — зацепиться за последний вагон, чтобы потом ехать снаружи.
С другой стороны, Rust только начинает развиваться. И у новичков, которые зайдут в этот поезд прямо сейчас, есть уникальная возможность развиваться вместе с ним, отслеживая все нововведения и разворачивающиеся вокруг дискуссии.
Но это будет лишь текущий статический срез, который не даст представление о best practices и общепринятых способах решения проблем.

Так если мы пишем кодовую базу с нуля, то нам как раз таки и нужен статический срез свежака без всего это легаси. Нам и нужен именно тот самый Modern C++. И нет нужды в знании старого С++. Вам нужно знание более старого С++ только если вы работаете с кодовой базой, которая также на нём написана. Я не вижу смысла для себя в 2020 знать, как там проблемы решались в С++98 (хоть и знаю, так как раньше работал с таким кодом, но теперь я с ним не сталкиваюсь).
Только тогда человек точно знает: как, почему и для чего появилась та или иная возможность или особенность стандарта.

Я считаю такие знания бесполезными для пользователей. Он не должен думать о причинах, он должен знать средства для решения проблем, не более того. Причины, историю и так далее оставим ребятам в Комитете, это их хлеб.
Ни одна книга не даст этого опыта. Этот поезд уже ушёл, и максимум, на что можно рассчитывать — зацепиться за последний вагон, чтобы потом ехать снаружи.

Не вижу в этом ничего плохого. Если работаете только с новой кодовой базой — то почему бы и нет? Если приходится и старое трогать (а в любом развивающемся языке будет старая и новая кодовая база — это неизбежно), то по ходу дела можно и подучить. Тут ничего уникального у С++ нет.
С другой стороны, Rust только начинает развиваться. И у новичков, которые зайдут в этот поезд прямо сейчас, есть уникальная возможность развиваться вместе с ним, отслеживая все нововведения и разворачивающиеся вокруг дискуссии.

… И через парочку ревизий языка им придётся знать несколько Editions. То есть кардинально ситуация не поменялась :) Я не считаю, что пользователи должны за всем этим следить. Просто должны проверять к каждому Edition штуки, какие завезли, думать об апгрейде компилятора, если это возможно в их ситуации, обдумывать, как применять новые фишки. Зачем читать все эти дискуссии и так далее — без понятия.
Так если мы пишем кодовую базу с нуля, то нам как раз таки и нужен статический срез свежака без всего это легаси. Нам и нужен именно тот самый Modern C++.

А есть спецификация этого Modern С++? Ну понятно, что умные указатели надо использовать, а может чего ещё? Сдается мне, что даже в комитете не понимают текущий рекомендованный срез. Я этот вопрос уже задавал к статье о фишка C++ 2020 и как-то хорошего ответа не нашлось.

Ответа размером с книгу? И как умные указатели помогут мне в реализации IPC shared memory? А в качестве указателей на регистры (в микроконтроллерах)? Я имею в виду что обычные указатели никуда не делись, и для них найдется применение. :)
Ответа размером с книгу?


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

Да, видел уже эту ссылку. Жалко новичкам она не подойдет, но мне сгодится. Спасибо.

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

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

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


Если C++ изначально был достаточно целостен, а потом в него сверху накручивались дополнительные фичи (грубо говоря), то Rust изначально имеет стройную концепцию, которая реализована не полностью. Львиная доля сложности языка как раз состоит в понимании исключений и частных ограничений языка, которые сама концепция, кажется, иметь не должна. Отсутствие const generics (скоро будут), отсутствие специализации, отсутствие NLL (уже реализовано), нет HKT ни для типов, ни для лайфтаймов и так далее. У языка очень большая перспектива для развития именно по пути к своему внутреннему концепту, пока реализованному лишь отчасти.


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


Также в Rust-компиляторе разрабатывается слой MIR (mid-level intermediate representation), который в будущем позволит не только менять бекэнд (сейчас используется LLVM), но и "фронтэнд", могут появиться новыя языки, которые будут компилироваться в Rust MIR.

Если C++ изначально был достаточно целостен
Только для мало знакомых с историей. Сначала назывался «Си с классами». Прикрутили проволокой ООП к Си.
До этого был Си как развитый ассемблер. Примотали синей изолентой структурное программирование.
C/C++ это как раз стихийно созданное нечто, захватившее много пространства в ПО. Хотя были и есть более правильно устроенные языки, созданные архитекторами ПО, но…
Надеюсь, что Ржа создана хорошо. Но если это так, то, как показывает опыт, применение языка может быть ограничено.
Прикрутили проволокой ООП к Си.

Ну, в этом же и была его концепция, если кратко :)

Rust изначально имеет стройную концепцию, которая реализована не полностью. Львиная доля сложности языка как раз состоит в понимании исключений и частных ограничений языка, которые сама концепция, кажется, иметь не должна. Отсутствие const generics (скоро будут), отсутствие специализации, отсутствие NLL (уже реализовано), нет HKT ни для типов, ни для лайфтаймов и так далее

А что можно почитать подробнее про эти стройные концепции?

Можно посмотреть на Haskell и представить, как это все может заработать в императивном языке, заточенном под эффективность и без сборщика мусора )


Если серьезно, то одного единственного документа, наверное, нет. Но кое-что вырисовывается из набора RFC: https://github.com/rust-lang/rfcs/tree/master/text

Как раз было интересно, насколько далеко Rust может двигаться в сторону выразительности ФП. Возможно, мне не хватает знаний Haskell и теоретической базы, чтобы полностью понимать сравнение разных подходов, систем типов итп.


Например, есть критика Rust за отсутствие HKT, монад и do-нотации, что приводит к частным решениям (например, тут. Насколько я понимаю, HKT в Rust пока что не планируются, планируются GAT, которые (как говорит уважаемый 0xd34df00d) не являются полноценной заменой (увы, здесь моих знаний не хватает). Вот разработчик говорит, что do-нотация не очень совместима с императивной парадигмой. (Оффтоп: и зачем им return, break итп внутри do-блока?)

Да, все так. GAT не заменяет HKT, но это уже хоть что-то на пути в эту сторону. Ведется поиск, идут дискуссии и эксперименты. То есть имеются ориентиры, и развитие языка направлено к ним, хоть достигнуть некоторых из них и не получится (либо мы пока не знаем как).

(Оффтоп: и зачем им return, break итп внутри do-блока?)

Хаскелистам? return — это на самом деле аппликативный pure, «запихнуть готовое чистое значение в монаду». Это даже не ключевое слово, а обычная функция (пусть и являющаяся частью соответствующего тайпкласса). А break вообще разбивает список на две части по предикату, к циклам и управлению потоком выполнения он отношения не имеет.

Хаскелистам?
Да нет, растаманам разработчикам Rust из по ссылке из моего сообщения, в которой говорится, что do-нотация не ложится на императивную парадигму, в которой присутствует управление потоком выполнения (return, break, continue)

Просто управление становится чуть более абстрактным. Вон, смотрите, сколько абстракций для циклов: тыц.

Хаскель, кстати, так себе с точки зрения цельности и стройности.


А ещё можно посмотреть на ATS (но я не смотрел особо плотно).

Забыл добавить. По крайней мере на моём опыте я совершенно недавно начал писать абсолютно новую кодовую базу (то есть легаси ровным счётом ноль). Но как «нативный» ЯП я выбрал именно С++. Не потому, что я его лучше знаю (более чем уверен, что мне хватило бы 2-3 недель, чтобы на него пересесть более-менее нормально), а потому, что не нашёл нужных мне библиотек (там нет аналога SObjectizer) и просто потому, что Rust не решает проблем лучше С++, с которыми я сталкиваюсь в разработке. Повторюсь, именно те проблемы, с которыми сталкиваюсь я (у вас возможно и будет решать). Но у меня современный С++17 (я бы на С++20 переключился, но не считаю его достаточно стабильным), у меня CMake, Conan, статический анализ (который в том числе и ловит (хоть пока что и не все, как Rust) lifetime ошибки (но у меня пока что ни одной не поймал, потому что ни одной и не было. Не факт, что так будет у вас :)). В целом у меня достаточно современный стек, чтобы на него жаловаться.

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

А можно ещё раз попросить Вас дать (ссылку на) пример конфигурации открытых линтеров и прочих инструментов для C++, которые Вы считаете достаточными для нового проекта на современном C++?

> Это действительно хороший вопрос. Многие считают, что С++ уже не исправить. Лично я так не считаю и своим развитием С++ показывает, что он умеет эволюционировать. Смысл в том, что C++ сейчас как раз и движется в сторону многих вещей, которые Rust уже предлагает.

Прям интересно стало. Как вы предполагаете встроить borrow checker в С++? Потому что без этого либо производительность, либо безопасность будут страдать.
Т. е. вы предлагаете создать отдельное подмножество языка со своей семантикой несовместимой со всем остальным языком, которое будет гордо называться, например, C++ SAFE. Для которого будет своя стандартная библиотека, нужно будет писать враперы (склеивающий код) с существующими библиотеками на C++. И в чем смысл сего действа тогда? NIH синдром?

Может есть какие-то более практичные варианты?
Ахах, конечно есть
unsafe {
use std:: 
}

А если почитать ссылку, то для D —
Новый синтаксис не добавляется. В сгенерированный код не вносится никаких изменений.
Да, да! Нового синтаксиса не добавляется а live функции что-такое? А в C++ они тоже есть? Если вы не заметили то я про C++ спрашивал, а не про D.
И по вашей ссылке
> Если функции @ live, вызывают функции, не являющиеся @ live, ожидается, что эти вызываемые функции будут представлять собой @ live-совместимый интерфейс, хотя он не проверяется
Предполагаем что все хорошо, но не проверяем. Как-то попахивает текущим плюсовым походом и безопасность стремительно улетучивается.

> Ахах, конечно есть
Без предоставления стандартной библилотеки, удовлетворяющей правилой новой семантики у вас будет куча кода unsafe (почти весь) и чего вы добьетесь в этом случае? Правильно, ничего.
@ live — это всего лишь атрибут, а механизм атрибутов в С++ тоже есть, даже стандартизовали.

Про «ахах конечно есть» это был намёк про Раст, библиотеки которого сплошь ансейф, если Вы не поняли.

Так что имеем картину — А если он миленький, то какая разница? (с) =)

Тут ниже уже приводили пример, как эти самые "просто атрибуты" выглядят в C++. И, глядя на этот пример, что-то Rust резко ещё более простым и понятным стал :-)

@ live — это всего лишь атрибут, а механизм атрибутов в С++ тоже есть, даже стандартизовали.


Получается атрибут live, по сути, берет и превращает текущий C++ в другой язык с другой семантикой (аля С++ SAFE)
И что это меняет относительно моей изначальной посылки? Текущий код и код существующих С++ библиотек не станет автоматически совместим с семантикой нового языка, и нужна будет обернутая/новая стандартная библиотека и враперы для текущих библиотек нужно будет писать или сами библиотеки переписывать.

Про «ахах конечно есть» это был намёк про Раст, библиотеки которого сплошь ансейф, если Вы не поняли.


Не стоит преувеличивать. Как правило это либо биндинги к текущим плюсовым библиотекам (но там особо выбора нет как и скорее всего у условного C++ SAFE), либо unsafe сильно локализован, как правило отдельными небольшими функциями.
Получается атрибут live, по сути, берет и превращает текущий C++ в другой язык с другой семантикой (аля С++ SAFE)
Совсем нет.
В D он включает DFA для конкретных функций. А в C++ пошли другим путем — DFA для всего включается при ключе компиляции.

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

В D он включает DFA для конкретных функций. А в C++ пошли другим путем — DFA для всего включается при ключе компиляции.

Не особо важно на каком уровне включается механизм проверки. Важно как осущесвтляется переход от safe части к unsafe и обратно. И тут, по сути, я вижу только 2 подхода:
1. Перенос классического статического анализатора в компилятор. Который, легко заиспользовать. но он ничего не гарантирует
2. Разметка монструозными атрибутами кода (уж лучше бы новый синтаксис предлагали) с написанием оберток существующих библиотек (в том числе и стандартной библиотеке)

Не такое уж и громоздкое описание подходов получилось.

Приводить в пример D вообще, странно. Т. к. переход к borrow checker'у c языка с GC совсем не тоже самое что переход с языка с ручным управлением памятью.
Да, ладно! И стандартная библиотека в D может без GC может работать? И утечки памяти не будет, если его отключить и не включать GC?
Фобос — нет (сильно ограниченно), а вот BetterC — да.
Кроме того, возвращаясь к Расту — режим no_std (BetterC) — предпочтителен для эмбеддед и для риалтайма.

Собственно, я по этой причине — возможности выбора (и смешивания) модели памяти (ручное, GC, RAII, RC), считаю D гораздо лучшим выбором для прикладного программирования и реальным конкурентом для системного.

Какое отношение ваши пассажи имеют к сложности прикручивания OB к языку? Собственно, прикручивать что-то такое к языку который поддерживает разные способы управления памятью, существенно сложнее, чем к языку, построенному лишь на одной единственной модели, какая бы она ни была.


Вот ещё ссылка по теме: https://habr.com/ru/post/460671/

Получается, сам спросил, сам и ответил =)
О чем спор — решительно непонятно, извиняюсь за резкость.
Оба варианта совмещаются.

Пока я вижу только 3 системы с DFA — C++, D, Rust. Они и обсуждаются. Возможно, скоро к ним присоединится Nim.
Мне было интересно «вдруг умные мужики придумали рабочий вариант внедрения borrow checker'a». Но пока что для меня оба эти подхода выглядят очень сомнительно. Не верится мне что они будут работать, что ими будут пользоваться.
Кстати, другие языки никогда не получат такой же контроль, как в Расте — не стоит и надеяться.

Ведь ОВ система защищает вас от удара молотком по пальцам самым простым и надежным способом — просто отрежьте вторую руку, и работайте одной =)
Как новчику начать писать на C++?(эт если че не сарказм, вдруг решу попробовать) Для старта с растом, ты открываешь растбук, она довольно короткая и неплохо так покрывает примитивные типы данных, методы работы и тд и тп. С C++, заходишь на хабр и видишь статью «17 способов инициализировать переменную». Если ты стоишь перед выбором что учить, то учить C++ желания не возникает.
Насколько я понимаю мозилла предлагает его как безопасная замена C++

Мозила может и предлагает, но совершенно очевидно, что кода на популярных языках написано столько, что уйдут они не скоро. И переписывать весь код C++ на раст, конечно никто не будет, из-за мыслей мозиллы. Я любой язык рассматриваю не как замену другому языку, а как язык на котором можно решить ту или иную задачу, и что он меняет, C++, python, go не суть важно.
Как новчику начать писать на C++

Взять любой обучающий С++ ресурс и начать писать. Это может быть книга, видеокурс, whatever. К сожалению, у С++ нет своего бесплатного аналога Rustbook пока что (это вопрос для SG20, который должен решить это, имхо), но есть много уже готовых книг\курсов. Выбор тут явно есть. Ну а по статьям на хабре о 17 способах инициализации судить — это хохма какая-то :)
Если ты стоишь перед выбором что учить, то учить C++ желания не возникает

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

Скорее антимаркетинг, не зря про C++ появились шутки в стиле, «что бы выучить C++ за 24 часа, нужно выучить квант физику, вернуться назад на несколько лет, и выучить с++ к моменту настоящее+24 часа», тоже rust маркетологи придумали? А когда C++'у предпочитают java,c# etc, тоже маркетинг раста?
Взять любой обучающий С++ ресурс и начать писать.

Ну а по статьям на хабре о 17 способах инициализации судить — это хохма какая-то :)

— Сирота?
— Сирота :(
— Мама папа есть?
— Мама папа есть=)

Синтаксис у Раста в целом нормальный. Есть шероховатости, но они постепенно устраняются. Те, кто программируют на Rust, дискомфорта не испытывают. Я думаю вы, как и многие из новичков, путаете "плохой синтаксис" с "непонятная/непривычная семантика". Во всяком случае уже не раз были споры насчет синтаксиса и никто из критиков не предложил лучшего решения для выражения тех же сущностей так, чтобы новый синтаксис не рушил остальные конструкции языка в других местах. Может быть у вас есть конструктивные предложения?


Правила заимствования в Rust, параметрический полиморфизм с тайпклассами (типажами) и алгебраический тип данных с паттерн-матчингом делают этот язык принципиально отличным от C++. Такой "статический анализатор", который есть в Rust, невозможен в текущих версиях C++.

Герб Саттер предложил:


template<Pointer T>
void swap(T& a, T& b)
[[gsl::post(lifetime(deref_a,{deref_b}))]] [[gsl::post(lifetime(deref_b,{deref_a}))]]
{ .. }

Можете сравнить с вариантом из раста:


fn swap<T>(x: &mut T, y: &mut T) { .. }

Дмитрий, ваш вариант лучше. Он выглядит как структура данных. А если это данные, то это значит сравнительно легко будет автоматизировать их обработку. Попробуйте провернуть тоже самое с вариантами по выше, в которых явно присутствует синтаксический шум. Все таки статья Эрика как по мне была весьма полезна.

По ссылке такого кода нет Поиск не работает нормально (

Выглядит ужасно, да.

Но похоже, что затронет в основном библиотечный код, а не пользовательский.
«Safe mode» в rust как я понимаю просто является встроенным в язык статическим анализатором кода не особо отличающийся(?) от таких же анализаторов на с++.

Лично я о чём-то аналогичном borrow checker для C++, слышал только в контексте lifetime profiler от Герба Саттера, и он работает так себе, легко сконструировать код и с ложно-положительными, и с ложно-отрицательными результатами.

Несмотря на то, что я в расте новичок, готов утверждать, что с безопасностью (а точнее со способами выстрелить себе в ногу) у раста всё существенно лучше, чем в плюсах. В safe rust получить UB/segfault/data races не проще чем в каких-нибудь Java/Python/C#. Статический анализ тут не причём, это именно проверка валидности программа с точки зрения компилятора. Для сравнения — вы получаете в Джаве ошибку, когда класс имплементит интерфейс, но не реализует его метод. Такая ситуация может быть валидна синтаксически, но интерпретатор ваш код отвергнет.
При этом в Safe Rust, наверное, можно сделать всё что угодно, кроме FFI-вызовов. А Unsafe же приходится использовать весьма редко. Например я попробовал посмотреть в https://github.com/openethereum/openethereum — и там на ~120000 строк rust-кода нашлось 22 вхождения слова unsafe (из которых некоторые даже не относятся к коду). Так что если у вас всё же что-то пойдёт не так, то есть довольно мало мест, на которые надо обратить пристальное внимание.

Всё же прав был уважаемый автор статьи с разбором того доклада Полухина, что от таких докладов больше вреда чем пользы.


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


Если будет время, посмотрите лекции Кладова про Rust, там на простых примерах всё разбирается.


Начать можно отсюда:
https://youtu.be/WV-m7xRlXMs?t=1875


Метка времени по ссылке как раз на моменте, где приводится пример кода на C++ и Rust. В C++ вы получаете UB, в Rust такой код не компилируется с человеческим объяснением в чём программист не прав.


Это очень круто, когда можно писать код и не становиться с каждым разом всё больше и больше параноиком "а есть ли в моём коде UB и прострелы памяти? А всё ли я делаю правильно и ничего ли не забыл?". Даже опытные разработчики на C++ периодически совершают ошибки и наступают на одни и те же грабли. Rust освобождает от этой боли.

Знакомые плюсоиды на такое отвечают "Да такое никто никогда не напишет", "Видео же, что ошибка", "У меня в программе 99% ошибок в логике, а с памятью никогда никаких проблем не бывает".

(в n-ый раз, но всё-таки)


Хочу получить ошибку компиляции тут, не подскажете, с каким компилятором и какими ключами собирать?

Во-первых, в учебниках написано, что string_view не рекомендуется возвращать по значению, надо возвращать string, как раз из-за подобных случаев.

Во-вторых, надо таки читать учебники, чтобы писать программы правильно.

В-третьих, Вы слишком много ожидаете от _первого_ релиза DFA.

О, началась знакомая риторика.


В-третьих, Вы слишком много ожидаете от _первого_ релиза DFA.

Возможно, но для более простого примера — когда shortest и cut_prefix вызываются непосредственно — профилировщик лайфтаймов от Герба Саттера таки ловит ошибку, пусть и с ложно-положительным срабатыванием, а -fanalyzerнет, так что какие-то ожидания у меня всё-таки были.


Во-вторых, надо таки читать учебники, чтобы писать программы правильно.

Ух, сколько гонора. Гм, а не подскажете, где можно найти учебник, который бы покрывал C++17? C++ core guidelines — это не учебник и не завершённая вещь.


Во-первых, в учебниках написано, что string_view не рекомендуется возвращать по значению, надо возвращать string, как раз из-за подобных случаев.

То есть string_view::substr должен возвращать string, я понял. А если серьёзно — ну что не так с example_ok по ссылке выше? Тут создаётся временная строка, да, но результат cut_prefix заведомо держит ссылку на данные str, а не n_str, так что тут всё в порядке. Собственно, если расписать типы shortest и cut_prefix, то в Rust им можно приписать разные типы:


fn shortest<'a>(a: &'a str, b: &'a str) -> &'a str {
    if a.len() < b.len() { a } else { b }
}

fn cut_prefix<'a>(prefix: &str, s: &'a str) -> &'a str {
    if s.starts_with(prefix) {
        &s[prefix.len()..]
    } else {
        s
    }
}

То есть информация о связях между аргументом и результатом непосредственно выражена в типе. И компилятор Rust эту информацию эксплуатирует:


// компилируется
fn example_ok(n: u32, s: &str) -> &str {
    let n_str = n.to_string();
    cut_prefix(&n_str, s)
}
// не компилируется: "cannot return value referencing local variable `n_str`"
fn example_err(n: u32, s: &str) -> &str {
    let n_str = n.to_string();
    shortest(&n_str, s)
}

И это именно информация в типах, а не кросс-анализ потока исполнения: я могу заменить реализации shortest и cut_prefix на заглушки и всё равно получить ошибку компиляции в нужном месте. Та же логика работает и в случае вариантом example, который оперирует переданным коллбеком:


fn example_hof_ok(
    n: u32,
    s: &str,
    func: for<'a> fn(&str, &'a str) -> &'a str
) -> &str {
    let n_str = n.to_string();
    func(&n_str, s)
}

fn example_hof_err(
    n: u32,
    s: &str,
    func: for<'a> fn(&'a str, &'a str) -> &'a str
) -> &str {
    let n_str = n.to_string();
    func(&n_str, s)
}

То есть в example_hof_ok тип функции func прямо говорит, что возвращаемое значение связано лишь со вторым аргументом, а в example_hof_err функция func может возвращать данные, связанные с любым аргументом. example_hof_err не компилируется, и для проверки даже не нужно её вызывать. Ну и, опять таки из-за того, что отношения выражены в типах, мы можем вызвать example_hof_ok с cut_prefix и не можем с shortest (тык).


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

Я не понимаю простыню, поясните — или Вы не можете явно прописать причинно-логическую связь между вводом и выводом, или думаете, что компилятор может быть умнее Вас?? Или даже хотите ???

вы уверены что сможете с этим работать соблюдая какие-то вменяемые сроки? Пробовали действительно умные компиляторы — idris какой-нибудь, или ATS?

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

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

Вы про статический анализ в GCC 10? Это пока ещё довольно сырая штука, отлавливает далеко не всё, а на выхлоп по прежнему больно смотреть:


… Первоначально это было 1187 строк. Я исправлял различные ошибки и реализовывал больше упрощений, чтобы довести его до 170 строк.

Это по прежнему "костыль" в виде статического анализа, прикрученный сбоку, который будет давать false positive или пропускать ошибки в тех или иных случаях. Более того, мало просто добавить статический анализ в свой workflow, нужно закладывать время программиста на обработку его результатов.


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

В то время как Rust на уровне компилятора предоставляет гарантии корректности.
Да для 20% случаев ошибок, но цена этого что то вроде вашей девственности при _каждой_ компиляции.
Я с подобным не согласен.
Тут в натуре ошибка, итератор инвалидирован инвазивной операцией ((
Посмотрите на следующий код, который написан на языке, считающимся высокоуровневым:
sleep(5);

И сравните с тем, как то же самое поведение реализуется в Rust:
use std::{thread, time::Duration};
thread::sleep(Duration::from_secs(5));


Где вам понятнее, что происходит и где по-вашему вероятность ошибиться меньше? Мне кажется, что ответ очевиден.


Эм… в первом случае, очевидно, понятнее? Но судя по общему тону статьи, автор намекает на второй вариант, что весьма странно, как по мне.
По-моему тоже пример неудачный. Разве что `5` действительно вызывает некоторое разночтение, но т.к. обычно sleep в мс, то при модификации первого примера на sleep(5*MSEC_PER_SEC); всё становится очевидно и без этого жуткого оверхеда.
Если что, я желаю всяческих удач Расту и кармически несовместим с С++, но именно данный пример скорее похож на словоблудие Явы

Вот вы и попались: "так как обычно sleep в мс". Пример взят из проблемных мест PHP, там в sleep передается задержка в секундах.

Я и не спорю, что голые константы, в сочетании с дурацки спроектированными функциями, плохи.
Просто здесь Растовый вариант выглядит уж очень громоздким.
Справедливости ради, это просто демонстрация строгой типизации, которая много где есть. В любом типизированном языке можно написать sleep, принимающий на вход аргумент с типом «промежуток времени» и решить эту проблему; точно также как в расте можно написать sleep, принимающий на вход u32. Так что конкретно это не делает раст «универсальным» языком.

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

Да, типы объявляются достаточно лаконично. Но я все равно могу написать функцию sleep с аргументом u32.

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


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

Если взять язык, который реально озабочен производительностью, а не просто вид делает — то в C++ накладок нет. Также их можно избежать в C#, насколько я знаю. В JVM с этим проблемы, да, но если Вы пишете под JVM, то это не самая Ваша серьезная проблема. Ну а про скрипты и говорить нечего.
Нельзя избежать оверхеда на C# абстракции и рантайм вносят свою лепту. Можно только приближаться по производительности к нативному коду. И инструментарий языка будет ограничен практически только конструкция для работы на стеке предаллоцированные массивы, struct, ref struct и span'ы.

В сравнении с такими абстракциями программировать на расте гораздо веселее.
Если взять язык, который реально озабочен производительностью, а не просто вид делает — то в C++ накладок нет.

Там полно накладок с другим.

Ну то есть не относящиеся к производительности. ОК, принято.
Ну вот разве что restrict-проблемы, это да. Которые решаются использованием локальных переменных.

А можно пример, как локальны переменные с этим помогут? Особенно в случае char* вместо int*.

Я имею в виду что-то вроде этого. Компилятор перечитывает значения из a, потому что возможно что a == b. Поэтому приходится читать лишь один раз и кэшировать значение локально.

Как сравнивать два POD'а не почленно?


Потому что сравнивать почленно — надеяться на компилятор, а он умудряется обделаться даже на предельно банальном случае:


struct Foo
{
    int f1;
    int f2;
};

bool operator==(const Foo& l, const Foo& r)
{
    return l.f1 == r.f1 && l.f2 == r.f2;
}

clang 9 компилирует в разумный


mov     rax, qword ptr [rdi]
cmp     rax, qword ptr [rsi]
sete    al
ret

а clang 10 (более новый, заметьте!) делает какую-то полную фигню


movq    xmm0, qword ptr [rdi]   # xmm0 = mem[0],zero
movq    xmm1, qword ptr [rsi]   # xmm1 = mem[0],zero
pcmpeqd xmm1, xmm0
pshufd  xmm0, xmm1, 80          # xmm0 = xmm1[0,0,1,1]
movmskpd        eax, xmm0
cmp     al, 3
sete    al
ret

Но это всё, конечно, не сравнится с gcc 10, вот где топ:


        mov     edx, DWORD PTR [rsi]
        xor     eax, eax
        cmp     DWORD PTR [rdi], edx
        je      .L5
        ret
.L5:
        mov     eax, DWORD PTR [rsi+4]
        cmp     DWORD PTR [rdi+4], eax
        sete    al
        ret

На этом языке предлагается писать производительный код? Серьёзно?

Да ладно, думаю Вы знаете :)
Код
bool operator==(const Foo& l, const Foo& r)
{
    return memcmp(&l, &r, sizeof(Foo)) == 0;
}

Вообще тут виноват компилятор ©, а не язык (ну или я опять о чем-то не знаю).
memcmp

Не имею права. А вдруг паддинг?


Вообще тут виноват компилятор ©, а не язык (ну или я опять о чем-то не знаю).

Ну так-то и хаскель — прекрасный для производительности язык, просто компилятор ещё недостаточно умный.

Если взять язык, который реально озабочен производительностью, а не просто вид делает — то в C++ накладок нет.

ИМХО самая большущая накладка — clone by default.

Ничто не заставляет. Но дополнительную когнитивную нагрузку "а скопируется ли здесь вектор или переместится?" создаёт.

Попробуйте в C# реализовать хотя бы что-то похожее на stack-allocated строчку, например. А то я наслышан историями из додо пиццы которые делали:


public struct String16 
{
    public char C1, C2, C3, C4, C5, C6, C7, C8, C9, C10, C11, C12;
}

Работать с этим, конечно, одно удовольствие.

Забавно, но это не совсем то. Это позволяет создать строку без промежуточного массива, но сама строка будет в хипе после создания. Вот одно из обсуждений, как видно, issue is open.

Смотреть IL чего? Этой функции? String это тип который хранится в хипе и не может храниться на стеке. Если функция возвращает стринг, то она может там хоть что делать, но вернёт она указатель на хип.

Ну что хранится на стеке, вернуть все равно нельзя. А вот использовать локально, а это гораздо больший процент переменных — выгодно.
Статья

Можно, если возвращать то что лежит на стеке по значению. Да и по ссылке можно передавать, если это не результат, а параметр.


Речь не про то, что возвращать или получать. А просто что тип string — хранит данные в хипе, так же, как растовый Vec, например. И никакой метод создания того или иного типа это изменить не может.




В итоге, я так и не увидел нигде "стековой строчки" которой можно было бы пользоваться. Самое близкое, что C# предлагает в 2020 году это Span<char>

Строку по значению, а куда копировать будем?

Я так понял из статьи, Span и используется.

Хотя полностью исключить аллокацию нельзя — можно минимизировать кол-во промежуточных.

Вот речь была про то как исключить полностью. Пример из раста:


use heapless::String;
use heapless::consts::U32;

fn main() {
   let mut stack_string = String::<U32>::new();
   stack_string.push_str("Hello");
   stack_string.push_str(" ");
   stack_string.push_str("world");
   stack_string.push_str("!");

   println!("{}", stack_string.as_str()); // не производит аллокаций, возвращает только ссылку.
}

Вот тут 0 аллокаций. И через as_str() мы можем работать как с обычной строкой, используя все методы из std. В шарпах увы так нельзя.

А где тут возврат строки из функции?

Стековый ShortString еще в Дельфи4 был, разве нельзя аналогичное сделать в шарпе?
Я вижу в статье
 Span<char> buffer = stackalloc char[length]; // DOES NOT heap allocate
А где тут возврат строки из функции?

Ну её очевидно можно вернуть из функции, у неё же тип указан.


Стековый ShortString еще в Дельфи4 был, разве нельзя аналогичное сделать в шарпе?

Нельзя


Я вижу в статье

Ну да, все верно, только вопрос что с этим буфером делать. Как например проверить что эта страка начинается с подстроки? В расте это будет:


let answer = stack_string.as_str().starts_with("Hello");
println!("Starts with 'Hello' = {}", answer);

А, майкрософт накопипастил реализацию для спанов из строк, неплохо. Ещё и продублировал, для ридонли спанов и обычных. Каждый метод минимум в 3 вариантах в итоге есть, оччень любопытно.


Ну, хоть что-то. Правда, додошники этим занимались до того, как спаны релизнулись в любом случае. Но знать не помешает, спасибо.

Ну, там не совсем копи-паст: основной алгоритм сравнения сделан для спанов, а тот, что сравнивает строки — просто делает AsSpan() обоим аргументам и делегирует всю работу первому.

Ну её очевидно можно вернуть из функции, у неё же тип указан.
Неочевидно, например str — нельзя, неизвестен размер.
Как тут сделано — непонятно, но можно. Судя по адресам — происходит копирование.

Упс, интересный побочный эффект получился. Я [опять] сломал безопасный Раст :'(
fn test() -> String::<U9> {
   let mut stack_string = String::<U9>::new();
   stack_string.push_str("Hello");
   stack_string.push_str(".");
   stack_string.push_str("world");
   stack_string.push_str("!.");

   stack_string
}
..
println!("{}", test()); 
Вывод: Hello.!.

Надо было делать stack_string.push_str(«world»).unwrap();

Но ведь повреждения памяти не было? Кажется, от дыр в логике раст защищать не обещал.


Кстати, варнинги вы принципиально не смотрите?


warning: unused std::result::Result that must be used
note: this Result may be an Err variant, which should be handled

Вы ничего не сломали, я для простоты примера не стал писать обработку ошибок. Если очень хочется, то так:


fn test() -> Result<String::<U9>, не помню тип ошибки> {
   let mut stack_string = String::<U9>::new();
   stack_string.push_str("Hello")?;
   stack_string.push_str(".")?;
   stack_string.push_str("world")?;
   stack_string.push_str("!.")?;

   Ok(stack_string)
}
..
println!("{:?}", test()); 

Только это не "слом" раста, а обычный код, работает как ожидается.

Не компилируется
Compiling rstack v0.1.0 (C:\VSProjects\rstack)
error[E0277]: the `?` operator can only be used in a function that returns `Resu
lt` or `Option` (or another type that implements `std::ops::Try`)
--> src\main.rs:18:4
|
16 | / fn test() -> String:: {
17 | | let mut stack_string = String::::new();
18 | | stack_string.push_str(«Hello»)?;
| | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot use the `?` operator in a funct
ion that returns `heapless::string::String<typenum::uint::UInt<typenum::uint::UI
nt<typenum::uint::UInt<typenum::uint::UInt<typenum::uint::UTerm, typenum::bit::B
1>, typenum::bit::B0>, typenum::bit::B0>, typenum::bit::B1>>`
19 | | stack_string.push_str(".");
… |
25 | | stack_string
26 | | }
| |_- this function should return `Result` or `Option` to accept `?`
|
= help: the trait `std::ops::Try` is not implemented for `heapless::string::S
tring<typenum::uint::UInt<typenum::uint::UInt<typenum::uint::UInt<typenum::uint:
:UInt<typenum::uint::UTerm, typenum::bit::B1>, typenum::bit::B0>, typenum::bit::
B0>, typenum::bit::B1>>`
= note: required by `std::ops::Try::from_error`
use heapless::String;
use heapless::consts::U9;

fn test() -> Result<String::<U9>, ()> {
    let mut stack_string = String::<U9>::new();
    stack_string.push_str("Hello")?;
    stack_string.push_str(".")?;
    stack_string.push_str("world")?;
    stack_string.push_str("!.")?;
 
    Ok(stack_string)
 }

fn main() {
    println!("{:#?}", test());
}
Я тоже с трудом победил. Первая симпатичная сигнатура — результат — сиськи =)
Result<(), ()>
Почему с трудом?
Просто объявляем в сигнатуре, что именно хочем получить на выходе — результат, либо ошибку. Тип ошибки написан в документации push_str или в сообщении компилятора.

Не так и очевидно. Я вот, увидев первый вариант, сходу не увижу сколько именно длится ожидание — 5 секунд или 5 миллисекунд. Просто потому что на разных языках (и даже просто на разных ОС в одном языке) разные умолчания.


Хотя пример всё равно кривой, потому что начиная с C++14 этот код должен выглядеть как-то так:


#include <thread>
#include <chrono>

std::this_thread::sleep_for(5s);

Здесь C++ тоже демонстрирует хороший дизайн, но исходный пример со sleep взят из PHP.

Тем временем в другом языке, не поддерживающем user-defined literals:


import core.thread;
alias seconds = dur!"seconds";
alias sleep = Thread.sleep;

// ...

5.seconds.sleep;

Это если говорить только о стандартной библиотеке.

Видимо на playground нет этой библиотеки. Но релизуется идея просто.
Spoiler header
use std::thread;
use std::time::Duration;

pub trait NumericalDuration {
    fn seconds(self) -> Duration;
}

impl NumericalDuration for u32 {
    fn seconds(self) -> Duration {
        Duration::from_secs(self as u64)
    }
}

#[allow(unused)]
use NumericalDuration as _;

fn main() {
    thread::sleep(1.seconds());
}

Победил, это оказывается требует очередной внешней бета-версии библиотеки
extern crate time;
use time::NumericalStdDurationShort as _;
use std::thread;
use std::time::Duration;

fn main() {
    assert_eq!(5.seconds(), Duration::from_secs(5));
    thread::sleep(15.seconds());
}

sleep(5) это спать 5секунд, 5 миллисекунд или 5 тиков? Как мне это понять, не заглядывая в документацию? А этот слип из какой библиотеки? Точно из стд, а вдруг нет? Не забыли никаких импортов?


Так что я лично выбираю вариант №2.

Как мне это понять, не заглядывая в документацию?

Не стыдно?

Не-а. Документация устаревает и врёт, а типы всегда актуальны и чекаются компилятором, который, в отличие от человека, не отвлекается и не устаёт.

Т.е. Вы полагаете, что документацию можно не читать? И так должно все быть понятно?

Ну вот в C++14 и в Rust аналогичный код понятен без документации. Почему это плохо?

Документацию к чему? Тут правильно сказали, в зависимости от языка программирования тут может быть 3 разных сценария. Мне вот по работе приходится постоянно переключаться между C#/TS/Rust/Python..., и в каждом работает по-разному. Например, в питоне это секунды, а в TS — миллисекунды. Поэтому код sleep(5) можно означать совершенно разные вещи тупо в зависимости от того, какое расширение у файлика который мы сейчас читаем.


И я меньше всего хочу получать ошибки из-за того, что я невыспался и забыл что вот вот в этом языке в этом контексте этот инт это не просто инт, а специальная штука которую нужно правильно использовать. Особенно если это не стандартный слип, а какой-нибудь самописный, который перекрывает дефолтную реализацию и имеет другую семантику. Зачем мне думать об этом, если за меня может это всё проверить тайпчекер? Вас не лень делать работу, которая машина сделает быстрее и намного лучше и меня, и вас, и любого другого человека?

Все делают ошибки, я не являюсь исключением. Но ни делание ошибок, ни избегание ошибок не являются самоцелью.
Меня смущает зацикленность защитников и апологетов Rust на безопасности и борьбе с ошибками. Пара знакомых, профессионально пишущих на Rust в нем привлекает строгость и выразительность языка и его системы типов. То же самое я вижу в публикациях признанных корифеев языка. Напирать на то, что язык не дает «отстрелить ногу», с моей точки зрения странно. Не принимаю это за главное в языке.
Помнится, апологеты ранних версий Java любили обсасывать ошибки освобождения памяти без GC. Не понимая того, что GC — особая стратегия управления памятью со своими плюсами и минусами, а отсутствие необходимости писать free/delete один из плюсов, причем, не самый важный.
Мне кажется, с Rust похожая история, второй принцип Цермело в действии, так сказать. Rust прекрасен, но не безопасностью.
Все делают ошибки, я не являюсь исключением. Но ни делание ошибок, ни избегание ошибок не являются самоцелью.

Для меня написание кода без ошибок который выполняет поставленную задачу как раз является самоцелью


Меня смущает зацикленность защитников и апологетов Rust на безопасности и борьбе с ошибками.

А я не апологет раста. Мне идрис там больше нравится, или даже хаскель. Раст я беру только туда, куда я не могу позволить себе взять ГЦ.


Напирать на то, что язык не дает «отстрелить ногу», с моей точки зрения странно. Не принимаю это за главное в языке.

А вы попробуйте. Когда можно писать код, и если он неправильный, то компилятор сообщит. Я вот тут недавно статью переводил: парсите, а не валидируйте. Вот тут конкретно пример из этой статьи: нужно число парсить в дату, а не заниматся валидацией "на глазок" (и уж тем более не использовать сырое значение без проверки). Выносить логические ошибки "ой, я вместо секунд миллисекунды написал" в типы — это то, чем надо заниматься. Пример, когда ракетар разбивается из-за того что метры с футами перепутали или рентген аппарат убивает своего клиента — это всё вот от этого.


Мне кажется, с Rust похожая история, второй принцип Цермело в действии, так сказать. Rust прекрасен, но не безопасностью.

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

Для меня написание кода без ошибок который выполняет поставленную задачу как раз является самоцелью
Так вопрос не в этом, раст нужен для того, чтобы писать производительный код. С другой стороны, неважно, насколько код быстрый,
Противоречие
Мне тоже понятнее второй. В первом варианте у меня сразу возникают вопросы:
1) Кто спит и с кем?
2) Пять — это время или число человек? Или размер кровати?
Итого: 2 WTF/line для первого варианта и 0 WTF/line для второго.
Про поддержку ООП в Расте, есть свежие новости?
(очень нравится Раст, но смущает ООП)

Как небыло, так и нет. И не будет )
Но у меня к вам 2 вопроса:
1) Что вы понимаете под ООП?
2) А это вам точно нужно?

Мне бы пригодилось. Чёртова уйма существующего кода завязана на наследовании и отношении is-a. Последний раз, когда я знакомился с темой, в Rust это делалось либо через Deref, либо через жуткие костыли, либо "тебе это не нужно".

В Rust не принято использовать полиморфизм подтипов, вместо этого есть параметрический полиморфизм.

Я понимаю. Но что делать с очередной внешней библиотекой на C-with-classes?

Я в свое время отказался от Паскаля итп, потому что все нужные либы нужно было «оборачивать».
А в Расте с этим еще хуже, а на результат автогенерации смотреть — вытекают глаза (тут про Дум была статья).
Спасибо за статью, давно интересуюсь Rust-ом, единственное что останавливает от «потратить время на изучение» — отсутствие поддержки не латинских (кириллических) имён. У меня вся бизнес модель — прикладная -> соответственно все имена переменных, объектов привязаны к наименованию объектов документооборота и бизнес логике — переводить их на английский и обратно просто невозможно, я думаю у многих прикладников есть такая проблема.

В своё время обещали добавить поддержку не латинских переменных, но прошло по моему уже года 1,5-2 и шевелений нет. Они в принципе планируют что то делать или лучше забить на Rust и не ждать?

Чтобы в программе случайно не оказалось слово Measure в значении "мера регулирования" или PhysicalPerson в значении "физлицо".

Only those users with full accounts are able to leave comments. Log in, please.