LINUX.ORG.RU

Интесресное свойство BASH. Модель Rust + Bash.

 ,


0

3

Интересное свойство bash/shell открыл для себя. Решил попытаться вникнуть в язык Rust. Для примера взял старый, измученный мной, алгоритм перебора перестановок Нараяны. Переписал его и уткнулся в то, что не так легко и элегантно новичку найти способ перевести аргумент командной строки в число. В C89 с приведением типов как-то просто. Искал варианты: все те, что нашёл, относительно синтаксиса и на мой субъективный взгляд, показались трудными для восприятия. Вдруг осенило меня, - можно же использовать в качестве инициализирующего скрипта bash. И появилась такая модель:

  1. Перебор перестановок на Rust с самым простым синтаксисом: данные пользователя хранятся в векторах, в самом скрипте.
  2. Стартовый скрипт на Bash создаёт копию основной программы, забирает аргументы командной строки, создаёт новые векторы и перезаписывает их в основной программе с помощью sed. Безопасно и просто. Результат: https://github.com/dcc0/permutations_rust

Ответ на: комментарий от anonymous-angler

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

Проблема some_string.parse() состоит в том, что чтобы добраться до собственно int-а и дальше с ним работать по существу, придётся поднасрать в код упоминаниями того, что вместо int-а могла вернуться ошибка. Растоманы, после нескольких лет мучений (с unwrap и с try!), сократили это упоминание до единственной кракозябрины - "?". В результате код на расте испещрен бесполезными (с точки зрения той логики, которую код пытается реализовать) кракозябрами.

Но ещё хуже то, что постоянные приплясывания вокруг Result — это не единственный источник бойлерплейта и кракозябр в расте...

Manhunt ★★★★★
()
Последнее исправление: Manhunt (всего исправлений: 2)
Ответ на: комментарий от Manhunt

Ты не с той стороны смотришь. В C++ приходится документировать все возможные исключения для каждой функции, документация может быть неполной/устаревшей/отсутствовать, программист может забыть/покласть на обработать исключения… А в расте все возможные ошибки - часть типа функции, они автоматически указываются в документации, и компилятор следит, чтобы программист не забыл их обработать. Автоматизация.

После раста писать на языках с ислючениями какой-нибудь код, работающий с недоверенными данными - страх и ужас. Никогда не знаешь, в каком месте всё взорвётся. И нет, один большой try...catch - это не обработка ошибок, это «отыбись от меня, компилятор».

anonymous
()
Ответ на: комментарий от Aswed

Я питонист, для меня это срам и ужас.

Владимир 123

anonymous
()
Ответ на: комментарий от Manhunt

Мне кажется, что это преувеличение проблемы. Если логика подразумевает, что должен быть int, то почему приходится парсить строку? А если логика подразумевает что нужно парсить строку, то обработка ошибок не бесполезна и уместна.

anonymous-angler ★☆
()
Ответ на: комментарий от anonymous-angler

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

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

Manhunt ★★★★★
()
Ответ на: комментарий от anonymous

А ты не с той стороны смотришь на исключения, это не return код, не надо ничего документировать, это ИСКЛ-АЯ ситуация, всю операцию надо считать неудавшейся, и там либо повторять целиком, либо еще что-то. Я вообще стд типами пользуюсь и никогда не делаю многопунктный case блок. И дефолтный терминейт() вполне годный обработчик (если я не пользуюсь исключениями вместо return). Да и асм там наверняка засерается у раста.

pavlick ★★
()
Последнее исправление: pavlick (всего исправлений: 1)
Ответ на: комментарий от Manhunt

Окей, попробуем ещё раз.

Ты парсишь и обрабатываешь ошибки парсинга где-то в сторонке. Затем, полученные результаты ты используешь без необходимости обрабатывать ошибки. Чем этот вариант тебе плох?

anonymous-angler ★☆
()
Ответ на: комментарий от anonymous

Ты не с той стороны смотришь. В C++ приходится документировать все возможные исключения для каждой функции, документация может быть неполной/устаревшей/отсутствовать, программист может забыть/покласть на обработать исключения… А в расте все возможные ошибки - часть типа функции, они автоматически указываются в документации, и компилятор следит, чтобы программист не забыл их обработать. Автоматизация.

Обычно есть ряд конкретных ошибок (или конкретных семейств ошибок), которые нужно содержательным образом обрабатывать в определенных местах в программе. И есть места в программе, где нужно обрабатывать «все прочие» ошибки, причем эта обработка уже не зависит от природы конкретной ошибки. Такого, чтобы нужно было отдельно обрабатывать каждую из возможных ошибок из каждой (например библиотечной) функции - мне никогда не встречалось. Автоматизировать, получается, особо-то и нечего.

Manhunt ★★★★★
()
Ответ на: комментарий от anonymous-angler

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

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

И если парсится конфиг, например, я предпочту видеть что-то полезное, вроде «line 10, column 40 contains unexpected symbol: …», а не «Failed to parse config».

anonymous-angler ★☆
()
Ответ на: комментарий от anonymous-angler

Ты парсишь и обрабатываешь ошибки парсинга где-то в сторонке. Чем этот вариант тебе плох?

Если код парсинга не будет загроможден обработкой исключительных ситуаций (например, упоминаниями о том, что при попытке чтения из файла бывают low-level I/O error) - то вполне норм.

Manhunt ★★★★★
()
Последнее исправление: Manhunt (всего исправлений: 1)
Ответ на: комментарий от Manhunt

Да. Но мне нравится в С одна вещь, используя только stdio можно собрать вектор/массив. Не очень удобно с точки зрения пользователя, но попробуйте придраться к корректности. Исключения не трогаю, хотя тут вроде бы достаточно только длину проверить:

[code=c] int main (int argc, char *argv[]) {

    int i = 0;
    int vector[100]={0};

    for (i = 0; i < argc ; i++) {
    vector[i] = i;
    }

for (i = 0; i < argc ; i++) {
    printf ("%d", vector[i]);
    }

} [/code]

P.S. В моей модели всё в перспективе можно обработать башем.
А чистый алгоритм лежит в Rust без нагромождений. И доступен для понимания, словно кристалл, слезинка чистоты.

AnonymUser
() автор топика
Последнее исправление: AnonymUser (всего исправлений: 4)
Ответ на: комментарий от Manhunt

Если код парсинга не будет загроможден обработкой … например, упоминаниями о том, что при попытке чтения из файла бывают low-level I/O error

Кто же смешивает код ввода-вывода с кодом парсинга?

DonkeyHot ★★★★★
()
Ответ на: комментарий от DonkeyHot

Если код парсинга не будет загроможден обработкой … например, упоминаниями о том, что при попытке чтения из файла бывают low-level I/O error

Кто же смешивает код ввода-вывода с кодом парсинга?

1. Бывает, что парсер рассчитан на обработку данных, которые не помещаются в памяти. Код-то может быть и не смешан, но парсер может запрашивать очередной блок входных данных, что повлечет ввод-вывод (естественно, с потенциальными ошибками ввода-вывда).
2. Это был первый попавшийся пример ошибки, не связанной непосредственно с логикой парсинга.

Manhunt ★★★★★
()
Последнее исправление: Manhunt (всего исправлений: 2)

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

[code=rust]

let args: Vec<String> = env::args().collect();

let mut vector: [i32; 10] = [0; 10];

let mut vector_reversed: [i32; 10] = [0; 10];

let n	=	args.len();

let mut i = 0;

let mut y = 0;

let mut x = n as i32;

loop {

vector[i] = y;

vector_reversed[i] = x;

if i == n {break}
i = i + 1;
y = y + 1;
x = x - 1;
}

println!("{:?}", vector);
println!("{:?}", vector_reversed)

[/code]

Вопросы и к получению аргументов и к объявлению массива. Я про вид.

Ещё странных непривычностей и непривычных странностей:

[code=rust] error[E0308]: mismatched types –> p_p.rs:15:14 | 15 | vector[i] = i; | ^ expected i32, found usize | help: you can convert an usize to i32 and panic if the converted value wouldn’t fit | 15 | vector[i] = i.try_into().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error

For more information about this error, try rustc --explain E0308 [/code]

Т.е. чтобы присвоить массиву значение в цикле, мне требуется создать еще одну переменную или написать так:

[code=rust] vector[i] = i.try_into().unwrap(); [/code]

[code=rust]let mut x = n as i32; [/code] Вот это конечно конструктивно приятно. Весьма, так сказать…

AnonymUser
() автор топика
Последнее исправление: AnonymUser (всего исправлений: 3)

Модель Rust + Bash. Что бы это ни значило, оно не заработает, потому что две ядерных аудитории не пересекаются ну никак.

anonymous
()
Ответ на: комментарий от Manhunt

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

WatchCat ★★★★★
()
Последнее исправление: WatchCat (всего исправлений: 1)
Ответ на: комментарий от DarkEld3r

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

Siborgium ★★★★★
()
Ответ на: комментарий от Manhunt

парсер может запрашивать очередной блок входных данных

Ввод-вывод в одном месте, парсер в другом. Это никак не связанные вещи.

while (read_block (&block, &io_err) && !io_err) {
    if (!parse_block (&block, &state, &parse_err) || parse_err) {
        break;
    }
}
if (io_err) {
    handle_io_err (&io_err);
}
if (parse_err) {
    handle_parse_err (&parse_err);
}
Siborgium ★★★★★
()
Последнее исправление: Siborgium (всего исправлений: 1)

Очередной унылый тред, в котором ЛОР будет сраться до 15-20 страниц.

anonymous
()
Ответ на: комментарий от Siborgium

Зачем же так нагло врать.

В расте код ревьются не хуже, а то и лучше чем в плюсах или сишке.

И, естественно, занимаются этим в основном сами же маинтейнеры крейтов.

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

anonymous
()
Ответ на: комментарий от anonymous

В расте код ревьются не хуже, а то и лучше чем в плюсах или сишке.

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

И, естественно, занимаются этим в основном сами же маинтейнеры крейтов.

Напомни, сколько людей использовали небезызвестный actix-web, наполненный unsafe?

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

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

Siborgium ★★★★★
()
Ответ на: комментарий от Siborgium

Про плюсы и сишку я вообще ничего не говорил.

А я сказал.

Вперед, доказывай свое утверждение.

Упомянутый тобой актикс в конце-концов проревьюили. и поубирали большинство ансейвов а дыра в https://www.opennet.ru/opennews/art.shtml?num=54678 продержалась 9 лет.

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

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

anonymous
()
Ответ на: комментарий от Siborgium

Никто не проводит никаких

Это ложь. Учёт двырявости ведётся здесь https://rustsec.org/ . И вроде как была ещё какая-то другая организация, которая как раз и занимается тем, что пишет сюда репорты и авторам постит Issues. Не говоря уже про сообщество, которое ведёт себя как инквизиция.

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

И вообще, принципиальной разницы между монолитом в 100500 строк кода и сотней крейтов по 1005 строк - нет.

Или ты считаешь иначе?

anonymous-angler ★☆
()
Ответ на: комментарий от anonymous

Упомянутый тобой актикс в конце-концов проревьюили.

Только когда «бахнуло» и после публичного линчевания автора. До этого было всем насрать, кушали и просили добавки.

пример ЯП где с ревью дела обстоят лучше.

Клоун, я тебе ничего не должен давать. Ты начал что-то приплетать и приписывать к моему тезису – сам ищи свои примеры. Я вообще про другие ЯП ни слова не написал.

Siborgium ★★★★★
()
Ответ на: комментарий от anonymous-angler

Учёт двырявости ведётся здесь https://rustsec.org/ . И вроде как была ещё какая-то другая организация, которая как раз и занимается тем, что пишет сюда репорты и авторам постит Issues

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

К тому же Rust поощряет написание тестов,

Это не совсем так – cargo предоставляет удобный инструмент для этого, но никак не поощряет.

которые есть в каждом первом хоть сколько-нибудь нужном крейте.

Так, несколькими нажатиями клавиш, @anonymous-angler вычеркнул треть самых загружаемых крейтов из «нужных».

Siborgium ★★★★★
()
Ответ на: комментарий от Siborgium

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

До тебя не доходит, что там длинный список УЖЕ есть, значит кто-то УЖЕ что-то нашёл и отревьюил? Весь смысл аргумента был в том что бы опровергнуть твоё «Никто не проводит никаких ревью».

Это не совсем так – cargo предоставляет удобный инструмент для этого, но никак не поощряет.

Возможно ты и прав.

Так, несколькими нажатиями клавиш, @anonymous-angler вычеркнул треть самых загружаемых крейтов из «нужных».

Так, несколькими нажатиями клавиш @Siborgium сделал голословное заявление.

anonymous-angler ★☆
()
Ответ на: комментарий от anonymous-angler

там длинный список УЖЕ есть, значит кто-то УЖЕ что-то нашёл и отревьюил

Я пролистал примерно до октября прошлого года. Хотя там есть дельные CVE, часто это мусорные репорты древних хелловорлдов на предмет мифических проблем или «X is unmaintained, use Y». Это не ревью, это перепаст issues с гитхаба.

сделал голословное заявление.

Нет конечно – зайди на crates.io и проверь крейты из списка самых загружаемых на предмет наличия тестов.

Siborgium ★★★★★
()
Последнее исправление: Siborgium (всего исправлений: 1)
Ответ на: комментарий от Manhunt

что в rust (в отличие от c++ и питона) исключений нету

А Result использовать тебе религия не позволяет?

Aswed ★★★★★
()
Ответ на: комментарий от Siborgium

У всех трех мажорных компиляторов поддерживается вываливание стек трейса.

Прошу прощения. Я был плюсовиком, когда этой фичи еще не было.

Нет, потому что чужой код никто не ревьюит.

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

Aswed ★★★★★
()
Ответ на: комментарий от Siborgium

Клоун, я тебе ничего не должен давать.

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

anonymous
()
Ответ на: комментарий от Siborgium

Нет конечно – зайди на crates.io и проверь крейты из списка самых загружаемых на предмет наличия тестов.

Клоун, я тебе ничего не должен давать.

очередное недоделаное трололо.

anonymous
()
Ответ на: комментарий от Siborgium

А на самом деле есть std::panic::set_hook, который задает обработчик для panic!.

А ещё есть panic::catch_unwind.

DarkEld3r ★★★★★
()
Ответ на: комментарий от DarkEld3r

есть только unrecoverable panic

Это неправда.

А как на самом деле?

Панику можно перехватить.

И panic от этого становится recoverable? С какой точки продолжится выполнение спаниковавшего потока?

Manhunt ★★★★★
()
Последнее исправление: Manhunt (всего исправлений: 2)
Ответ на: комментарий от Aswed

что в rust (в отличие от c++ и питона) исключений нету

А Result использовать тебе религия не позволяет?

А Result - это не исключения, это коды возврата. Разумеется, при разработке на Rust, ничего другого использовать и не получится. Здравствуйте кракозябры.

Manhunt ★★★★★
()
Последнее исправление: Manhunt (всего исправлений: 1)
Ответ на: комментарий от Siborgium

Никто не проводит никаких ревью того, что в этих крейтах написано.

Ну прямо таки никто. Коду какого-нибудь буста ты, наверное, доверяешь? А ведь большинство им пользуется точно так же - как чёрным ящиком, который «как-то работает». Или скажешь, что бустом пользуются (и пишут) серьёзные люди? Ну так и растовые библиотеки тоже разные.

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

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

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

DarkEld3r ★★★★★
()
Ответ на: комментарий от Siborgium

парсер может запрашивать очередной блок входных данных

Ввод-вывод в одном месте, парсер в другом. Это никак не связанные вещи.

while (read_block (&block, &io_err) && !io_err) {
    if (!parse_block (&block, &state, &parse_err) || parse_err) {
        break;
    }
}
if (io_err) {
    handle_io_err (&io_err);
}
if (parse_err) {
    handle_parse_err (&parse_err);
}



1. В твоем коде парсер не запрашивает данные.
2. Тело while() в твоем коде на 60% посвящено обработке ошибок и на 40% посвящено штатной работе программы. Вот такого смешивания как раз хотелось бы избежать: то есть сделать так, чтобы штатная работа была описана отдельно и не содержала плясок вокруг всех этих io_err/parse_err, а обработка ошибок - отдельно. Примерно вот так:

try
{
    while (read_block (&block)) {
        parse_block (&block, &state);
    }
}
catch (io_err_t io_err)
{
    handle_io_err (&io_err);
}
catch (parse_err_t parse_err)
{
    handle_parse_err (&parse_err);
}


Сравни, насколько легче теперь читается тело while()

Manhunt ★★★★★
()
Последнее исправление: Manhunt (всего исправлений: 1)
Ответ на: комментарий от Siborgium

Только когда «бахнуло» и после публичного линчевания автора.

Ну нет же. «Бахнуло» как раз потому что люди заглянули в код (поревьювали его). Автор сопротивлялся, ну а дальше вся эта драма и случилась. Но в итоге, как мне кажется, всё к лучшему: для актикса нашлись мейнтейнеры, автор ушёл писать свой «самый быстрый» форк. Ну и наконец есть альтернативные фреймворки.

DarkEld3r ★★★★★
()
Ответ на: комментарий от Siborgium

Так, несколькими нажатиями клавиш, @anonymous-angler вычеркнул треть самых загружаемых крейтов из «нужных».

Хочешь сказать, что у трети самых загружаемых крейтов вообще нет тестов? Или я не понял мысль?

DarkEld3r ★★★★★
()
Ответ на: комментарий от Manhunt

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

В частности, возникает вопрос: если read_block() возвращает false тогда, когда у него не осталось данных, то может быть после выхода из while() надо уведомлять парсер, что данных-то больше не будет? Вызывать какой-нибудь

parse_finalize(&state);

Manhunt ★★★★★
()
Ответ на: комментарий от AnonymUser

самый простой спооб получения аргументов на Rust выглядит местами впечатляюще, местами даже не знаю, как сказать:

Ты написал какой-то ужас. Чтобы просто развернуть переданные аргументы достаточно env::args().rev().collect().

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

Вопросы и к получению аргументов и к объявлению массива. Я про вид.

Если сможешь сформулировать вопросы - вероятно получишь ответы, а пока это какое-то нытьё вникуда.

DarkEld3r ★★★★★
()
Ответ на: комментарий от Siborgium

Нет конечно – зайди на crates.io и проверь крейты из списка самых загружаемых на предмет наличия тестов.

Зашёл, список из первых десяти крейтов выглядит вот так:

Вижу тесты в каждом крейте, что я делаю не так?

DarkEld3r ★★★★★
()
Ответ на: комментарий от Manhunt

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

И это прекрасно.

DarkEld3r ★★★★★
()
Ответ на: комментарий от Manhunt
  1. В твоем коде парсер не запрашивает данные.

Он ничего не должен запрашивать.

Тело while() в твоем коде на 60% посвящено обработке ошибок и на 40% посвящено штатной работе программы.

Тело while() было написано от балды, просто чтобы показать, что легко написать код, обработка ошибок которого производится отдельно.

Сравни, насколько легче теперь читается тело while()

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

Siborgium ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.