Option vs ? - критика Rust
От: Shmj Ниоткуда  
Дата: 21.10.23 04:17
Оценка: -1
Вот Rust гордится своим Option для всего — мол по умолчанию можно не опасаться, что будет NRE или попытка обратиться через nullptr.

А ведь вместо Option — гораздо удобнее и понятнее символ ? а так же сопутствующие ему — ! ?? и пр. — что уже фактически стало стандартом — используется и в C# и в Dart совершенно одинаково и интуитивно можно сказать понятно.

Ведь Rust отстает, получается.
Re: Option vs ? - критика Rust
От: Zhendos  
Дата: 21.10.23 09:29
Оценка: +2
Здравствуйте, Shmj, Вы писали:

S>Вот Rust гордится своим Option для всего — мол по умолчанию можно не опасаться, что будет NRE или попытка обратиться через nullptr.


S>А ведь вместо Option — гораздо удобнее и понятнее символ ? а так же сопутствующие ему — ! ?? и пр. — что уже фактически стало стандартом — используется и в C# и в Dart совершенно одинаково и интуитивно можно сказать понятно.


S>Ведь Rust отстает, получается.


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

if (a != null) 
  return a.something; 
else
  return null;


В C# же "." не запрещен. Option это аналог анотации [CanBeNull] ,
которую проверяет не ReSharper, а компилятор.
Re: Option vs ? - критика Rust
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 21.10.23 11:38
Оценка: 1 (1) -1
Здравствуйте, Shmj, Вы писали:

S>Вот Rust гордится своим Option для всего — мол по умолчанию можно не опасаться, что будет NRE или попытка обратиться через nullptr.

Это ведь семантика

S>А ведь вместо Option — гораздо удобнее и понятнее символ ? а так же сопутствующие ему — ! ?? и пр. — что уже фактически стало стандартом — используется и в C# и в Dart совершенно одинаково и интуитивно можно сказать понятно.

А это синтаксис, он действительно не очень

S>Ведь Rust отстает, получается.

Да, с точки зрения синтаксических возможностей раст довольно бедный язык. ? оператор есть, а ?? нет. И ! нет, надо везде писать .unwrap().
Нет оператора is как C#, предлагается через громоздкий if let делать.
А еще нет перегрузок функций, дефолтных параметров, нет функций с переменным числом параметров.
Операции с указателями крайне неудобны, так как операция разыменовывания имеет меньший приоритет и нет оператора -> как в C. Вроде как сделано для того, чтобы люди меньше писали unsafe, но некоторые вещи сложно эффективно выразить в safe.
Это все чисто синтаксические недостатки.

Есть еще и проблемы в семантике: работа с Error, пробрасывание ошибок и стектрейсы для error, отсутствие наследования (которое в каждом втором фреймфорке переизобретено). Отсутствие стандартного метода работы с метаданными и вообще отсутствие высокоуровневых концепций в стандартной библиотеке, async прибитый к конкретному движку.

Вроде есть макросы для борьбы со всеми неудобствами, но макросы зачастую создают проблем больше, чем решают.
Re[2]: Option vs ? - критика Rust
От: Zhendos  
Дата: 21.10.23 12:50
Оценка: +1 -1
Здравствуйте, gandjustas, Вы писали:

S>>Ведь Rust отстает, получается.

G>Да, с точки зрения синтаксических возможностей раст довольно бедный язык. ? оператор есть, а ?? нет. И ! нет, надо везде писать .unwrap().

"unwrap" как раз не надо везде писать, он нужен для тестов или "main" функции,
"unwrap" в коде функции как раз антипатерн.

G>Нет оператора is как C#, предлагается через громоздкий if let делать.

G>А еще нет перегрузок функций, дефолтных параметров, нет функций с переменным числом параметров.

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

И неявная передача дополнительного параметра (дефолтные параметры),
или наличие функций с одинаковыми именами, но потенциально разным поведением в одном контексте (перегрузка)
это однозначно плохо.

Из-за совместимости с С функции с переменным числом параметров как раз поддерживаются:
extern "C" {
    pub fn printf(__format: *const ::std::os::raw::c_char, ...) -> ::std::os::raw::c_int;
}


G>Операции с указателями крайне неудобны, так как операция разыменовывания имеет меньший приоритет и нет оператора -> как в C. Вроде как сделано для того, чтобы люди меньше писали unsafe, но некоторые вещи сложно эффективно выразить в safe.


Указатель можно превратить в ссылку одним "unsafe", а потом обращаться ко всем полям через ".",
и эффективность от этого никак не пострадает.

G>Есть еще и проблемы в семантике: работа с Error, пробрасывание ошибок и стектрейсы для error,


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

G>отсутствие наследования (которое в каждом втором фреймфорке переизобретено).


Наследование-то есть. Но наследование поведения, а ООП как раз о поведении.
Наследования данных действительно нет, но для ООП это и не нужно, просто так исторически
сложилось, что классы наследовали и поведение и данные.

G>Отсутствие стандартного метода работы с метаданными и вообще отсутствие высокоуровневых концепций в стандартной библиотеке,


Здесь не понял о чем речь.

G>async прибитый к конкретному движку.


Это не правда, "async" в embedded применяется,
там не просто "tokio" нет, там даже стандартной библиотеки не используется,
а "async" прекрасно работает. Там же по сути концепций раз два и обчелся: Waker, Context и Future.
И их может кто угодно использовать, как это может считаться "прибитым к конкретному движку" непонятно
Отредактировано 10.10.2024 18:43 Zhendos . Предыдущая версия .
Re[3]: Option vs ? - критика Rust
От: vsb Казахстан  
Дата: 21.10.23 12:54
Оценка: :)
Здравствуйте, Zhendos, Вы писали:

Z>На мой взгляд, очерненный C++, лучше все-таки явные ошибки, а не исключения,

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

В Rust-е ещё не начали писать panic-safe код? (:
Re[3]: Option vs ? - критика Rust
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 21.10.23 13:49
Оценка: -1
Здравствуйте, Zhendos, Вы писали:

Z>Здравствуйте, gandjustas, Вы писали:


S>>>Ведь Rust отстает, получается.

G>>Да, с точки зрения синтаксических возможностей раст довольно бедный язык. ? оператор есть, а ?? нет. И ! нет, надо везде писать .unwrap().

Z>"unwrap" как раз не надо везде писать, он нужен для тестов или "main" функции,

Z>"unwrap" в коде функции как раз антепатерн.
Например я пишу приложение, которое pdf файлы разбирает на кусочки. Если друг не получилось прочитать или записать файл, то программа должна упасть. У меня нет других вариантов обработки этой ошибки. Причем упасть желательно со стектрейсом. Почему в этом случае я не должен использовать unwrap?

G>>Нет оператора is как C#, предлагается через громоздкий if let делать.

G>>А еще нет перегрузок функций, дефолтных параметров, нет функций с переменным числом параметров.

Z>Я бы назвал это как раз достоинствами. В областях где нужна надежность,

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

Твоя вера сильна.



Z>И неявная передача дополнительного параметра (дефолтные параметры),

Z>или наличие функций с одинаковыми именами, но потенциально разным поведением в одном контексте (перегрузка)
Z>это однозначно плохо.
Говорят, если белое долго называть черным, то некоторые даже поверят.

Z>Из-за совместимости с С функции с переменным числом параметров как раз поддерживаются:

Z>
Z>extern "C" {
Z>    pub fn printf(__format: *const ::std::os::raw::c_char, ...) -> ::std::os::raw::c_int;
Z>}
Z>

Ты же не можешь в расте сделать свою функцию с переменным числом параметров. Даже если ты её потом экспортируешь.

G>>Операции с указателями крайне неудобны, так как операция разыменовывания имеет меньший приоритет и нет оператора -> как в C. Вроде как сделано для того, чтобы люди меньше писали unsafe, но некоторые вещи сложно эффективно выразить в safe.


Z>Указатель можно превратить в ссылку одним "unsafe", а потом обращаться ко всем полям через ".",

У тебя есть указатель на структуру А, в которой есть поле — указатель на B, напиши как будет выглядеть обращение к этому полю.

Z>и эффективность от этого никак не пострадает.

Аминь


G>>отсутствие наследования (которое в каждом втором фреймфорке переизобретено).

Z>Наследование-то есть. Но наследование поведения, а ООП как раз о поведении.
То что в расте вы называете наследованием поведение — это экстеншн-методы в извращенной форме.

Z>Наследования данных действительно нет, но для ООП это и не нужно, просто так исторически

Z>сложилось, что классы наследовали и поведение и данные.
Ага, не нужно. Открываем доку https://rust-cli.github.io/book/tutorial/cli-args.html прямо с оф сайта видим:
use clap::Parser;

/// Search for a pattern in a file and display the lines that contain it.
#[derive(Parser)]
struct Cli {
    /// The pattern to look for
    pattern: String,
    /// The path to the file to read
    path: std::path::PathBuf,
}

fn main() {
    let args = Cli::parse();
}


Упс, наследование поведения через макросы. Даже ооп как такового нет, а наследование есть.

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

G>>Отсутствие стандартного метода работы с метаданными и вообще отсутствие высокоуровневых концепций в стандартной библиотеке,

Z>Здесь не понял о чем речь.
Где в стандартной библиотеке json, http client, http server, unicode. Так чтобы их не стесняясь использовать и в создании своей библиотеки?


G>>async прибитый к конкретному движку.

Z>Это не правда, "async" в embedded применяется,
Z>там не просто "tokio" нет, там даже стандартной библиотеки не используется,
Не понимаю твою мантру. Ты хочешь сказать что можно написать веб-сервис, а потом например заменить tokio на async-std, не переписывая прикладной код?

Z>а "async" прекрасно работает. Там же по сути концепций раз два и обчелся: Waker, Context и Future.

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

Z>И их может кто угодно использовать, как это может считаться "прибитым к конкретному движку" непонятно

В этом и проблема.
Re[2]: Option vs ? - критика Rust
От: SkyDance Земля  
Дата: 21.10.23 17:21
Оценка:
G>Это все чисто синтаксические недостатки.

Не спора ради.
Я встречал прямо противоположное мнение: синтаксис должен быть максимально простым. Если какие-то операции ну очень часто встречаются (тот же error bubble wrapping в отсутствие exceptions), тогда надо делать синтаксис (и получатся все те же, хаха, exceptions).

Так же и с Rust: если в него добавить все эти "??", "!" и прочие удобства, будет существенно сложнее понимать написанный код (т.к. он будет более condensed). А также сложнее будет изначально выучить язык. Я вот до сих пор в C# путаюсь с этими всеми символами.
Re[3]: Option vs ? - критика Rust
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 21.10.23 20:05
Оценка:
Здравствуйте, SkyDance, Вы писали:

SD>Я встречал прямо противоположное мнение: синтаксис должен быть максимально простым.

Видимо от тех кто пишет на Го
Им же кайф все время писать
res, err := f()
if err != nil {
    return nil,err
}


(кстати идо сих пор не могу запомнить чем := отличается от просто =)

SD>Если какие-то операции ну очень часто встречаются (тот же error bubble wrapping в отсутствие exceptions), тогда надо делать синтаксис (и получатся все те же, хаха, exceptions).

В Rust сделали оператор ? чтобы не писать как в Go. При этом сделали не сразу, кажись после 1.0 добавили.

А вот до операторов ! вместо .unwrap() и ?? (||) вместо .or_else() еще не додумались.


SD>Так же и с Rust: если в него добавить все эти "??", "!" и прочие удобства, будет существенно сложнее понимать написанный код (т.к. он будет более condensed).

Я дважды проходил через это:
Сначала было в C#, когда ввели ?., ?? и ??=.
Потом такое же ощущение поймал, когда изучал zig, там обилие ?, ! и * в разных местах сильно смущает.

Сначала непонятно и непривычно, но стоит один раз написать программу с этими операторами и становится все сильно проще.

SD>А также сложнее будет изначально выучить язык. Я вот до сих пор в C# путаюсь с этими всеми символами.

В C# не так много символов чтобы путаться, странно что это вызывает проблемы.
Re[3]: Option vs ? - критика Rust
От: T4r4sB Россия  
Дата: 21.10.23 21:38
Оценка:
Здравствуйте, Zhendos, Вы писали:

Z>Указатель можно превратить в ссылку одним "unsafe", а потом обращаться ко всем полям через ".",

Z>и эффективность от этого никак не пострадает

Ага, иногда эффективность даже может повыситься, если компилятору покажется, что этот указатель не надо читать повторно (а на самом деле надо). И тогда после оптимизации программа поменяет поведение. С unsafe надо очень хорошо думать.
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re[2]: Option vs ? - критика Rust
От: Shmj Ниоткуда  
Дата: 22.10.23 03:06
Оценка:
Здравствуйте, Zhendos, Вы писали:

Z>В C# же "." не запрещен. Option это аналог анотации [CanBeNull] ,

Z>которую проверяет не ReSharper, а компилятор.

В Dart — не дает скомпилировать даже. В C# по умолчанию выдает warning, но можно сделать чтобы тоже не давало скомпилировать вот так:

  <PropertyGroup>
    ...
    <Nullable>enable</Nullable>
    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
  </PropertyGroup>
Re[4]: Option vs ? - критика Rust
От: SkyDance Земля  
Дата: 22.10.23 03:32
Оценка: +1
G>Видимо от тех кто пишет на Го
G>Им же кайф все время писать <error bubble wrap>

Я же специально написал: error bubble wrapping — тот самый случай, когда exceptions переизобретаются в очередной раз (и в очередной раз криво, т.к. теряется стек, и непонятно, где же возникла исходная ошибка, — nil и nil, угадывай сам).

Но то я — противник tuple return (ибо считаю, что для читаемости функция должна всегда возвращать значение, а все ошибки должны идти по другому каналу, т.е. exceptions). Для фанатов противоположного подхода нужно не просто tuple типа {ok, Ret} и {error, Reason}, а полновесные {error, func_name, [Arg1, Arg2], line_no, {caused_by, inner_func_name, [Arg1], line_no1, {caused_by, ...}}}}}}. Ну то есть тот же самый стек, только не в виде exception, а прямо в коде возврата.

Для такой цели, разумеется, нужны те самые exceptions. Пусть и в таком вот explicit виде, когда стек едет рядом с return value.

G>(кстати идо сих пор не могу запомнить чем := отличается от просто =)



О чем я и писал: не всегда сразу очевидно, где нужно ?, где ??, и нет ли какого-нибудь ?| (ведь нет же, а?)

G>В Rust сделали оператор ? чтобы не писать как в Go. При этом сделали не сразу, кажись после 1.0 добавили.


Именно это и есть правильный подход. Не сразу все подряд лепить, а тщательно выбирать, что нужно и что не очень.

G>А вот до операторов ! вместо .unwrap() и ?? (||) вместо .or_else() еще не додумались.


Погоди, C# не сразу строился.

G>В C# не так много символов чтобы путаться, странно что это вызывает проблемы.


А кто чуть выше написал вот это: не могу запомнить чем := отличается от просто =
Re[5]: Option vs ? - критика Rust
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 22.10.23 10:24
Оценка:
Здравствуйте, SkyDance, Вы писали:

G>>Видимо от тех кто пишет на Го

G>>Им же кайф все время писать <error bubble wrap>

SD>Я же специально написал: error bubble wrapping — тот самый случай, когда exceptions переизобретаются в очередной раз (и в очередной раз криво, т.к. теряется стек, и непонятно, где же возникла исходная ошибка, — nil и nil, угадывай сам).

А чем этот случай лучше чем "do or die" или "мамой клянусь" оператор? Или "value or else" ? Они все примерно в том же контексте используются

G>>(кстати идо сих пор не могу запомнить чем := отличается от просто =)


SD>

SD>О чем я и писал: не всегда сразу очевидно, где нужно ?, где ??, и нет ли какого-нибудь ?| (ведь нет же, а?)
Я не пишу на Go, поэтому запомнить не могу. А на C# пишу и не испытываю проблем со чтением всех современных операторов.


G>>В Rust сделали оператор ? чтобы не писать как в Go. При этом сделали не сразу, кажись после 1.0 добавили.

SD>Именно это и есть правильный подход. Не сразу все подряд лепить, а тщательно выбирать, что нужно и что не очень.
А с другой стороны есть zig, где сразу все значки добавили и выглядят он там органично и непротиворечиво.

G>>В C# не так много символов чтобы путаться, странно что это вызывает проблемы.

SD>А кто чуть выше написал вот это: не могу запомнить чем := отличается от просто =
Фишка в том, что в C# в разных контекстах используется ?. и ?? не получится заменить один на дугой и получить код похожий на валидный. А в го с = и := — вполне.
Re[6]: Option vs ? - критика Rust
От: Быдлокодер  
Дата: 22.10.23 11:27
Оценка: +2
Здравствуйте, gandjustas, Вы писали:


G>Фишка в том, что в C# в разных контекстах используется ?. и ?? не получится заменить один на дугой и получить код похожий на валидный.


А как бы Вы предложили реализовать оператор ?? в Rust? Насколько я понимаю, Option — это обычный enum, и у значения None нет какой-то особой поддержки на уровне языка, в отличии от null в C#.
Так же, насколько я понимаю, в идиоматическом Rust операторы присваивания встречаются редко, соответственно и потребность в ?? тоже ниже.

Оператор ! уже занят, для указания типов, которые не возвращают значения. Да и в языке просто нет нулевых ссылок.
fn inf() -> ! {
        loop {
        
        }
    }
Отредактировано 22.10.2023 11:30 Быдлокодер . Предыдущая версия .
Re[3]: Option vs ? - критика Rust
От: Быдлокодер  
Дата: 22.10.23 11:37
Оценка:
Здравствуйте, Shmj, Вы писали:


S>В C# по умолчанию выдает warning, но можно сделать чтобы тоже не давало скомпилировать вот так:


В теории да, но на уже существующем большом проекте сделать будет затруднительно.

Можно ли считать такой код идеоматическим? Можно ли рассчитывать, что вся команда готова и умеет писать в подобном стиле?
Здорово, что в C# появляются такие возможности! Но используются они в продакшене, кажется, лишь энтузиастами и передовыми командами.
На Rust же просто не сможешь писать иначе.
Отредактировано 22.10.2023 11:38 Быдлокодер . Предыдущая версия .
Re[7]: Option vs ? - критика Rust
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 22.10.23 11:41
Оценка: +1
Здравствуйте, Быдлокодер, Вы писали:

Б>Здравствуйте, gandjustas, Вы писали:



G>>Фишка в том, что в C# в разных контекстах используется ?. и ?? не получится заменить один на дугой и получить код похожий на валидный.


Б>А как бы Вы предложили реализовать оператор ?? в Rust? Насколько я понимаю, Option — это обычный enum, и у значения None нет какой-то особой поддержки на уровне языка, в отличии от null в C#.


Как сокращение для unwrap_or_else, то есть вместо opt.unwrap_or_else(|| value) писать opt ?? value


Б>Оператор ! уже занят, для указания типов, которые не возвращают значения. Да и в языке просто нет нулевых ссылок.

Основная проблема не типах, а в том, что ! используется в макросах.
Хотя было бы неплохо писать opt! вместо opt.unwrap() особенно в цепочках выражений.
Макросы я бы заменил на символ @ в началае.
Re[6]: Option vs ? - критика Rust
От: SkyDance Земля  
Дата: 22.10.23 17:57
Оценка:
G>А чем этот случай лучше чем "do or die" или "мамой клянусь" оператор?

do or die получается само собой при unhandled exception. Превратить в "or die" можно что угодно, это несложно. В обратную сторону же приходится колхозить.

G>Я не пишу на Go, поэтому запомнить не могу. А на C# пишу и не испытываю проблем со чтением всех современных операторов.


Мне приходится писать на всем подряд, от Erlang до Javascript. Читать — на еще бОльшем количестве языков. Держать в памяти, где в каком языке какие хитрые операторы, не получается. Что-то из памяти всегда выскакивает (как, к примеру, постоянно забываю про named arguments в C#).

G>А с другой стороны есть zig, где сразу все значки добавили и выглядят он там органично и непротиворечиво.


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

G>Фишка в том, что в C# в разных контекстах используется ?. и ?? не получится заменить один на дугой и получить код похожий на валидный.


Я скорее не про "писать", а про "читать". Мне читать надо много больше, чем писать.
Re: Option vs ? - критика Rust
От: Разраб  
Дата: 23.10.23 07:32
Оценка: 3 (1) :)
Здравствуйте, Shmj, Вы писали:

S>А ведь вместо Option — гораздо удобнее и понятнее символ ? а так же сопутствующие ему — ! ?? и пр. — что уже фактически стало стандартом — используется и в C# и в Dart совершенно одинаково и интуитивно можно сказать понятно.


https://youtu.be/CR9mLGN9jh0?t=930
☭ ✊ В мире нет ничего, кроме движущейся материи.
Re: Option vs ? - критика Rust
От: sergii.p  
Дата: 23.10.23 09:30
Оценка:
Здравствуйте, Shmj, Вы писали:

S>Ведь Rust отстает, получается.


конкретно тут отстаёт. Но надо учесть, что указатели в Rust встречаются куда реже чем в C# и Java и для их обработки не надо вводить синтаксический сахар. Тот же проброс ошибок — явление более частое, так что довольно быстро озаботились оператором ?. Вы приведите конкретный код, где вам бы хотелось применить элвис-оператор и вам расскажут как это идиоматично переписать на Rust. Лично я потребности в таком операторе не испытываю (хотя на шарпе часто даже злоупотребляю).
Re[7]: Option vs ? - критика Rust
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 23.10.23 10:28
Оценка: -1
Здравствуйте, SkyDance, Вы писали:

G>>А чем этот случай лучше чем "do or die" или "мамой клянусь" оператор?


SD>do or die получается само собой при unhandled exception.

Это когда есть в языке исключения. А когда это раст, то там паттерн "or die" очень нужен.

SD>Превратить в "or die" можно что угодно, это несложно.

Проблема исключительно в синтаксисе

Вот пример кода на раст:
let ext = path.extension().unwrap().to_str().unwrap();
let path = path.with_extension(format!("{}.{}", page_index, ext));
doc.save(path).unwrap();

На три строки три .unwrap(), который реализует паттерн or_die? и это еще лайтово, бывает по три .unwrap на строку.
Гораздо лучше смотрелось бы так:
let ext = path.extension()?!.to_str()?!;
let path = path.with_extension(format!("{}.{}", page_index, ext));
doc.save(path)?!;

Где ?! — это сахар для .unwrap(), чтобы не было коллизий с макросами.

G>>Я не пишу на Go, поэтому запомнить не могу. А на C# пишу и не испытываю проблем со чтением всех современных операторов.


SD>Мне приходится писать на всем подряд, от Erlang до Javascript. Читать — на еще бОльшем количестве языков. Держать в памяти, где в каком языке какие хитрые операторы, не получается. Что-то из памяти всегда выскакивает (как, к примеру, постоянно забываю про named arguments в C#).

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

G>>А с другой стороны есть zig, где сразу все значки добавили и выглядят он там органично и непротиворечиво.

SD>Это пока не появится еще один паттерн, под который придется добавлять очередной значок (и там уже снова начнется колхоз).
Ну собственно так всегда и происходит, все языки так живут. Наоборот пытаясь добавить все значки на старте скорее всего получится мусор. В том же расте часть знаков, которые были в 1.0 поубирали, а другие добавили.


G>>Фишка в том, что в C# в разных контекстах используется ?. и ?? не получится заменить один на дугой и получить код похожий на валидный.

SD>Я скорее не про "писать", а про "читать". Мне читать надо много больше, чем писать.
Чем меньше букв написано, тем проще читать в итоге. Знать язык все равно надо.
Re[8]: Option vs ? - критика Rust
От: sergii.p  
Дата: 23.10.23 10:59
Оценка: +1
Здравствуйте, gandjustas, Вы писали:

G>Гораздо лучше смотрелось бы так:

G>
G>let ext = path.extension()?!.to_str()?!;
G>let path = path.with_extension(format!("{}.{}", page_index, ext));
G>doc.save(path)?!;
G>

G>Где ?! — это сахар для .unwrap(), чтобы не было коллизий с макросами.

чем так не устраивает? Или принципиально писать в стиле "or die"?

fn new_extension(path: &Path, page_index: u8) -> Option<PathBuf> {
    let ext = path.extension()?.to_str()?;
    Some(path.with_extension(format!("{}.{}", page_index, ext)))
}


Лично моё мнение, если язык делает синтаксический сахар для стиля "or die" с ним явно что-то не так.
Re[9]: Option vs ? - критика Rust
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 23.10.23 11:23
Оценка:
Здравствуйте, sergii.p, Вы писали:

SP>Здравствуйте, gandjustas, Вы писали:


G>>Гораздо лучше смотрелось бы так:

G>>
G>>let ext = path.extension()?!.to_str()?!;
G>>let path = path.with_extension(format!("{}.{}", page_index, ext));
G>>doc.save(path)?!;
G>>

G>>Где ?! — это сахар для .unwrap(), чтобы не было коллизий с макросами.

SP>чем так не устраивает?

Многословно, я же говор что пример выше — лайтовый.


SP>Или принципиально писать в стиле "or die"?

Что значит "принципиально" ? Это было бы удобнее и сократило бы объем кода от 20% до 30%.


SP>Лично моё мнение, если язык делает синтаксический сахар для стиля "or die" с ним явно что-то не так.

Что не так с zig?
Re[2]: Option vs ? - критика Rust
От: Sinclair Россия https://github.com/evilguest/
Дата: 23.10.23 11:42
Оценка: +1
Здравствуйте, Разраб, Вы писали:
Р>https://youtu.be/CR9mLGN9jh0?t=930
Ужасно. Просто ужасно. Чувак берёт плохой код, и лёгким движением руки.... делает его ещё хуже!
Пипец какой-то.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[10]: Option vs ? - критика Rust
От: sergii.p  
Дата: 23.10.23 11:48
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Что значит "принципиально" ? Это было бы удобнее и сократило бы объем кода от 20% до 30%.


вы похоже мой пример кода не увидели. Оператор ? поддерживает Option так же как и Result. Ваши "хотелки" уже есть в языке. Не надо придумывать проблемы там, где их нет. "unwrap" не нужен.
Re[11]: Option vs ? - критика Rust
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 23.10.23 12:30
Оценка: -1
Здравствуйте, sergii.p, Вы писали:

SP>Здравствуйте, gandjustas, Вы писали:


G>>Что значит "принципиально" ? Это было бы удобнее и сократило бы объем кода от 20% до 30%.


SP>вы похоже мой пример кода не увидели. Оператор ? поддерживает Option так же как и Result. Ваши "хотелки" уже есть в языке. Не надо придумывать проблемы там, где их нет. "unwrap" не нужен.


Ну конечно же нет и unwrap нужен. Я привел пример кода реального проекта. Если в нем что-то пошло не так, то мне не надо возвращать None или Err, мне надо чтобы программа завершилась и показала стактрейс. Никакие операторы ? в этом не помогут, увы. В приложении, не в библиотеке, unwrap более чем нужен. Вернее нужны исключения, которых в расте нет.
Re[12]: Option vs ? - критика Rust
От: SkyDance Земля  
Дата: 23.10.23 14:31
Оценка:
G>Вернее нужны исключения, которых в расте нет.

Вот тут двумя руками за.
Из-за того, что исключений нет, каждый колхозит свой вариант аналогичной функциональности, гоняя наколеночные стектрейсы в возвращаемой структуре.
Re[3]: Option vs ? - критика Rust
От: Быдлокодер  
Дата: 23.10.23 14:33
Оценка: +1
Здравствуйте, Sinclair, Вы писали:

S>Ужасно. Просто ужасно. Чувак берёт плохой код, и лёгким движением руки.... делает его ещё хуже!

S>Пипец какой-то.

А можете рассказать, что не так в преобразованном коде?
Отредактировано 23.10.2023 14:34 Быдлокодер . Предыдущая версия .
Re[4]: Option vs ? - критика Rust
От: Sinclair Россия https://github.com/evilguest/
Дата: 24.10.23 02:51
Оценка: 15 (5) +1
Здравствуйте, Быдлокодер, Вы писали:

Б>Здравствуйте, Sinclair, Вы писали:


S>>Ужасно. Просто ужасно. Чувак берёт плохой код, и лёгким движением руки.... делает его ещё хуже!

S>>Пипец какой-то.

Б>А можете рассказать, что не так в преобразованном коде?

1. В репозиторий вносится метод GetLast3YearsCompletedOrdersCountFor(long customerId).
Отлично — мы увеличили площадь поверхности репозитория, затрудняя его поддержку. Кроме того, теперь нам надо полагаться на средства рефакторинга для принятия простейших решений. Вот, например — требования к скидке изменились, теперь заказы берутся не за три года, а за два. Что делать — менять метод, или его кто-то уже использует?
Введение отдельного метода оправдано там, где есть хотя бы одно из
а) он используется более 1го раза
б) его "смысл" не совпадает с его текстом.
Здесь нет ни одного из этих вариантов.
Правильный ответ:

var completedOrdersInLast3Years = 
  from o in _merchantDb.Orders 
  where 
    o.customerId == ord.Customer ID &&
    o.StateID == OrderState.Completed &&
    o.OrderDate >= DateTime.UtcNow.AddYears(-3)
  select o;
var count = completedOrdersInLast3Years.Count();


Если прямая запись предикатов царапает глаз, то к IEnumerable<Order> делается набор екстеншн-методов, и код переписывается так:
var completedOrdersCount = 
  merchantDb.Orders
    .ByCustomerId(ord.CustomerId)
    .ByState(OrderState.Completed)
    .WithinLast(TimeSpan.FromYears(3));

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

2. У DiscountCalculator публичный метод называется CalculateDiscountBy(long). Это — заботливо разложенные грабли.
Вот в таком коде компилятор не заметит никакой ошибки. Заметит ли её ревьювер кода?
public void CheckoutV2(long orderId)
{
  var order = _ordersRepository.GetOrder(orderId);
  var discount = _discountCalculator.CalculateDiscountBy(orderId);
  order.ApplyDiscount(discount);
  order.State = OrderState.AwaitingPayment;
  _ordersRepository.Save(order);
}

Если уж хочется упороться, то метод должен называться CalculateCustomerDiscount, а типом параметра должен быть или специальный отдельный СustomerId, или интерфейс ICustomerReference, который реализуется всеми объектами, ссылающимися на customer — и, в частности, order.

3. Примерно те же проблемы у метода DiscountBy() — по его вызову непонятно, что туда должно передаваться.
Плюс жульничество: то, что однострочное выражение для расчёта уровня скидки переписано в цепочку if, читаемость улучшило незначительно.
Я вот вовсе не уверен, что такой код:
private decimal DiscountBy(long completedOrdersCount)
{
  if (completedOrdersCount >= 5)
    return 30;
  if (completedOrdersCount >= 3)
    return 20;
  if (completedOrdersCount >= 1)
    return 10;

  return 0;
}

читается сильно лучше, чем
private decimal DiscountBy(long completedOrdersCount) => 
  completedOrdersCount >= 5 
    ? 30
    : completedOrdersCount >= 3
      ? 20
      : completedOrdersCount >= 1
        ? 10
        : 0;

И уж точно это читается хуже, чем
private decimal DiscountBy2(long completedOrdersCount) => 
  completedOrdersCount switch 
  {
    >= 5 => 30,
    >= 3 => 30,
    >= 1 => 10,      
    _    =>  0
  };

После такой записи становится понятно, почему мы не хотим выносить этот код в отдельный метод.
В итоге, метод вычисления скидки превращается в 1 выражение:
public decimal CalculateCustomerDiscount(ICustomerReference cr) =>
  _merchantDb.Orders
    .ByCustomerId(cr.CustomerId)
    .ByState(OrderState.Completed)
    .WithinLast(TimeSpan.FromYears(3))
    .Count() switch 
    {
      >= 5 => 30,
      >= 3 => 30,
      >= 1 => 10,      
      _    =>  0
    };

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

3. Отдельная песня про сопровождаемость этого кода. В реальном приложении у нас явно будет не одна скидка, а целая маркетинговая система со сложным набором правил. Поэтому никакого DiscountCalculator, да ещё с прямыми зависимостями от сервиса, предоставляющего предысторию заказов, у нас не будет. А будет движок правил, которые применяются к отдельным позициям и к заказу в целом. Это позволит не звать программистов каждый раз, как отделу продаж приходит в голову идея "на второй товар в заказе — скидка 20%, на третий — скидка 50%". И весь DDD с его Ubiquitos language пойдёт в быстром темпе вдоль и по диагонали. Потому что упускает существенный момент: моделировать надо не задачу, а решение. Даже в таком микроскопическом примере мы уже должны увидеть, как будут устроены сигнатуры типов, моделирующих правила применения скидок.
Скорее всего, нам потребуется информация как о кастомере, так и о заказе.
Поэтому метод CalculateCustomerDiscount заменится на CalculateOrderDiscount(IOrderReference o) с прицелом на будущее заменить эту заглушку на полноценную систему скидочных правил с приоритетами и ограничениями.

4. Перформанс. В реальном нагруженном приложении нам крайне не хочется видеть подъёма в память целого заказа ради того лишь, чтобы взять из него customerID, сбегать куда-то ещё в базу, и потом обновить скидку, и выполнить обратный апдейт всей записи. Зачем?
У нас есть функция, которая по order строит его скидку.
Всё, что нужно сделать — это передать эту функцию на сторону СУБД:
public void CheckoutV3(long orderId)
{
  _merchantDb.Orders.ById(orderId)
    .Set(o=>o.Discount, _discountCalculator.CalculateCustomerDiscount)
    .Set(o=>o.State, o=> OrderState.AwaitingPayment)
    .Update();
}


PROFIT!
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Отредактировано 24.10.2023 2:52 Sinclair . Предыдущая версия .
Re[5]: Option vs ? - критика Rust
От: Разраб  
Дата: 24.10.23 04:49
Оценка:
Здравствуйте, Sinclair, Вы писали:
В целом прекрасная иллюстрация как надо.
S>Введение отдельного метода оправдано там, где есть хотя бы одно из
S>а) он используется более 1го раза
ну, если используется тестирование, всегда будет использоваться дважды (в проде и тесте).


S>читается сильно лучше, чем

S>
S>private decimal DiscountBy(long completedOrdersCount) => 
S>  completedOrdersCount >= 5 
S>    ? 30
S>    : completedOrdersCount >= 3
S>      ? 20
S>      : completedOrdersCount >= 1
S>        ? 10
S>        : 0;
S>

S>И уж точно это читается хуже, чем
S>
S>private decimal DiscountBy2(long completedOrdersCount) => 
S>  completedOrdersCount switch 
S>  {
    >>= 5 => 30,
    >>= 3 => 30,
    >>= 1 => 10,      
S>    _    =>  0
S>  };
S>


с момента появления матча на свиче все больше убеждаюсь что его сложнее читать чем код с иф или кейс.

S>Всё, что нужно сделать — это передать эту функцию на сторону СУБД:

S>
S>public void CheckoutV3(long orderId)
S>{
S>  _merchantDb.Orders.ById(orderId)
S>    .Set(o=>o.Discount, _discountCalculator.CalculateCustomerDiscount)
S>    .Set(o=>o.State, o=> OrderState.AwaitingPayment)
S>    .Update();
S>}
S>

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

ПС это чисто мои придирки делитанта
☭ ✊ В мире нет ничего, кроме движущейся материи.
Re[6]: Option vs ? - критика Rust
От: Sinclair Россия https://github.com/evilguest/
Дата: 24.10.23 05:45
Оценка: 3 (1)
Здравствуйте, Разраб, Вы писали:
Р>ну, если используется тестирование, всегда будет использоваться дважды (в проде и тесте).
Это тавтология. Эдак мы и метод Modulo2Equals0 будем считать "использованным дважды".
Юнит-тесты — это вспомогательный механизм. Сами по себе они не могут быть оправданием введения методов или чего-то ещё.

Р>с момента появления матча на свиче все больше убеждаюсь что его сложнее читать чем код с иф или кейс.

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

S>>Всё, что нужно сделать — это передать эту функцию на сторону СУБД:

S>>
S>>public void CheckoutV3(long orderId)
S>>{
S>>  _merchantDb.Orders.ById(orderId)
S>>    .Set(o=>o.Discount, _discountCalculator.CalculateCustomerDiscount)
S>>    .Set(o=>o.State, o=> OrderState.AwaitingPayment)
S>>    .Update();
S>>}
S>>

Р>опять к вопросу тестируемости, в чистом ДДД логика отделяется от базы именно с этой целью.
Не, чистый ДДД — это просто алкоголизм, даже на наркоманию не тянет.
Логика отделяется от базы при помощи отделения логики от базы. Для этого достаточно превратить CheckoutV3 из void-метода в функцию, которая возвращает IUpdatable<Order>.
И мы тестируем не "SQL на живой базе", и не "SQL с тестовой базой", и не "сервис с моком репозитория в памяти", а чистую функцию.
Задача функции — породить корректный SQL по заданным параметрам. Всё.
То, что корректный SQL корректно обрабатывается на стороне СУБД, протестировано за десятилетия до нас.

Р>ПС это чисто мои придирки делитанта
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Отредактировано 24.10.2023 5:46 Sinclair . Предыдущая версия .
Re[6]: Option vs ? - критика Rust
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 24.10.23 11:48
Оценка:
Здравствуйте, Разраб, Вы писали:

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


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

База данных в таком приложении — это ключевое. К ней будут обращаться через программу, так и через систему отчетности, которая не знает о вашей программе ничего, так и напрямую, в помощью excel, powerbi и прочих инструментов.

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

Концептуальная часть DDD — ubiquitous language — должна работать именно для БД — таблицы и колонки должны иметь названия, соответствующие терминам предметной области. Чтобы запросы, например для расчета скидки, читались не только разработчиком, но и бизнес-аналитиком далеким от деталей реализации конкретной программы.
То же самое касается и программы, которую вы пишите. Она должна генерировать запросы в тех же терминах, что используются в предметной области.

Зачем и вообще как можно в таких условиях "отделять логику от базы" ?
Re[7]: Option vs ? - критика Rust
От: Разраб  
Дата: 24.10.23 13:34
Оценка:
Здравствуйте, gandjustas, Вы писали:



G>Зачем и вообще как можно в таких условиях "отделять логику от базы" ?


Ну вот примерно так https://github.com/AntyaDev/Talks/blob/master/2017/tdd_on_F%23/src/Basket.Domain/Domain.fs
☭ ✊ В мире нет ничего, кроме движущейся материи.
Re[8]: Option vs ? - критика Rust
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 24.10.23 16:15
Оценка:
Здравствуйте, Разраб, Вы писали:

Р>Здравствуйте, gandjustas, Вы писали:




G>>Зачем и вообще как можно в таких условиях "отделять логику от базы" ?


Р>Ну вот примерно так https://github.com/AntyaDev/Talks/blob/master/2017/tdd_on_F%23/src/Basket.Domain/Domain.fs


И где там логика из-за которой заказчик будет платить вам деньги, а не возьмет типовой конструктор интерне-магазина?
Re[9]: Option vs ? - критика Rust
От: Разраб  
Дата: 25.10.23 03:20
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Здравствуйте, Разраб, Вы писали:


Р>>Здравствуйте, gandjustas, Вы писали:




G>>>Зачем и вообще как можно в таких условиях "отделять логику от базы" ?


Р>>Ну вот примерно так https://github.com/AntyaDev/Talks/blob/master/2017/tdd_on_F%23/src/Basket.Domain/Domain.fs


G>И где там логика из-за которой заказчик будет платить вам деньги, а не возьмет типовой конструктор интерне-магазина?

Тут основной профит, в полном детерминизме логики домена, тупой калькулятор.
взаимодействие с инфраструктурным кодом организовано через команды и запросы.
т.е. домен содержит то самое решение проблемы. но не более. работа с бд это же детали.
☭ ✊ В мире нет ничего, кроме движущейся материи.
Re[10]: Option vs ? - критика Rust
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 25.10.23 06:58
Оценка: +2
Здравствуйте, Разраб, Вы писали:

Р>Тут основной профит, в полном детерминизме логики домена, тупой калькулятор.

Не продал


Р>взаимодействие с инфраструктурным кодом организовано через команды и запросы.

Не продал х2

Р>т.е. домен содержит то самое решение проблемы. но не более. работа с бд это же детали.

Заказчика в кончном итоге интересует две вещи: что будет на экране и что будет в базе. Этом примере нет ни того, ни другого.
Re: Option vs ? - критика Rust
От: Разраб  
Дата: 25.10.23 08:51
Оценка:
Здравствуйте, Shmj, Вы писали:

S>Вот Rust гордится своим Option для всего — мол по умолчанию можно не опасаться, что будет NRE или попытка обратиться через nullptr.


S>А ведь вместо Option — гораздо удобнее и понятнее символ ? а так же сопутствующие ему — ! ?? и пр. — что уже фактически стало стандартом — используется и в C# и в Dart совершенно одинаково и интуитивно можно сказать понятно.


S>Ведь Rust отстает, получается.

ага, вот шарп:
await jsSoundUtils?.DisposeAsync(); // <= Не работает!!!
jsSoundUtils?.Dispose(); // а так работает! но кто-то решил и без IDisposable сойдет
☭ ✊ В мире нет ничего, кроме движущейся материи.
Re[2]: Option vs ? - критика Rust
От: Sinclair Россия https://github.com/evilguest/
Дата: 25.10.23 15:21
Оценка: +1
Здравствуйте, Разраб, Вы писали:
S>>Ведь Rust отстает, получается.
Р>ага, вот шарп:
Р>
Р>await jsSoundUtils?.DisposeAsync(); // <= Не работает!!!
Р>jsSoundUtils?.Dispose(); // а так работает! но кто-то решил и без IDisposable сойдет
Р>

Хм. Интересный кейз.

Попробуйте добавить в скоуп вот такой класс:
public static class NullableValueTaskHelper
{
  public static System.Runtime.CompilerServices.ValueTaskAwaiter GetAwaiter(this ValueTask? nullableValueTask)
    => (nullableValueTask ?? ValueTask.CompletedTask).GetAwaiter();
}

Есть мнение, что после этого await jsSoundUtils?.DisposeAsync() заработает.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[10]: Option vs ? - критика Rust
От: Sinclair Россия https://github.com/evilguest/
Дата: 26.10.23 02:23
Оценка: 1 (1)
Здравствуйте, Разраб, Вы писали:
Р>взаимодействие с инфраструктурным кодом организовано через команды и запросы.
Два раза перечитал — не нашёл ни команд, ни запросов.
Выглядит как очередное решение "в вакууме" — примерно столь же полезное, как и абстрактная ООР-шная модель корзинки.
Преимущества функционального подхода к решению задачи "давайте будем держать корзинку в памяти, и определим алгебру корзинок через операции над ними" мне известны, и я с ними не спорю.
Проблема в том, что к решению задачи нас это никак не приближает.
а) если манипулируемая сущность — это ViewModel, то нам алгебра над ней менее принципиальна, чем реактивность (возможность реализовать двусторонний биндинг путём подписки на события). Здесь я этого не вижу — как мы реализуем слабосвязанный с моделью GUI, который будет автоматически обновляться при модификациях корзины?
б) если манипулируемая сущность — это DataModel, то нам, опять же, принципиально как раз то, что происходит в базе данных. Подходы ORM с Change Tracking-ом — катастрофически неэффективны. Но они, по крайней мере, позволяют писать относительно понятный код. А как это реализовано в вашем примере? Каким-то внешним кодом, который должен уметь "сохранять корзинку в базу"?

Р>т.е. домен содержит то самое решение проблемы. но не более. работа с бд это же детали.

Это — опасное заблуждение. Детали — это как раз способ описания трансформаций БД. Не бывает в нормально спроектированных приложениях такого, что "мы сначала хранили всё в памяти, потом в одном большом JSON файле, потом перешли на реляционную СУБД". Всё наоборот — БД первична. Её структура может не меняться десятилетиями. Она — и есть домен. А вот эти все нюансы вроде того, как рассчитываются скидки, или там "можно ли добавить 5 единиц товара X к корзинке, в которой товара X не было" — это эфемерщина, которая меняется ежеквартально.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[3]: Option vs ? - критика Rust
От: Sinclair Россия https://github.com/evilguest/
Дата: 26.10.23 10:33
Оценка: 3 (1) +1
Здравствуйте, Sinclair, Вы писали:
S>Есть мнение, что после этого await jsSoundUtils?.DisposeAsync() заработает.
Но вообще лучше использовать не ручной вызов, а
await using(jsSoundUtils)
{
 ...
}

— у него такая же семантика в плане обработки jsSoundUtils == null, но есть гарантия "не забыть" диспоуз, и нет необходимости в костылях.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[4]: Option vs ? - критика Rust
От: DarkEld3r  
Дата: 07.11.23 12:06
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Например я пишу приложение, которое pdf файлы разбирает на кусочки. Если друг не получилось прочитать или записать файл, то программа должна упасть. У меня нет других вариантов обработки этой ошибки. Причем упасть желательно со стектрейсом. Почему в этом случае я не должен использовать unwrap?


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

G>То что в расте вы называете наследованием поведение — это экстеншн-методы в извращенной форме.


Почему? Чем реализация трейта отличается от реализации интерфейса? И чем наследование трейтов отличается от наследования интерфейсов?

Z>>Наследования данных действительно нет, но для ООП это и не нужно, просто так исторически

Z>>сложилось, что классы наследовали и поведение и данные.
G>Ага, не нужно. Открываем доку https://rust-cli.github.io/book/tutorial/cli-args.html прямо с оф сайта видим:
G>
G>use clap::Parser;

G>/// Search for a pattern in a file and display the lines that contain it.
G>#[derive(Parser)]
G>struct Cli {
G>    /// The pattern to look for
G>    pattern: String,
G>    /// The path to the file to read
G>    path: std::path::PathBuf,
G>}

G>fn main() {
G>    let args = Cli::parse();
G>}
G>


G>Упс, наследование поведения через макросы. Даже ооп как такового нет, а наследование есть.


То, что (некоторые) процедурные макросы навешиваются через derive ещё не делает это наследованием.

G>В этом и проблема.


В чём? Ну и всё-таки есть ли прибитость к движку или нет?.. Так-то в C# ты вообще "движок" поменять не можешь и ничего.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.