Pull to refresh
17
1
Subscribers
Send message

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

Ну, никто не мешает перед блоком написать комментарий, который описывает что в нём происходит. ИМХО это даже лучше, потому что с функцией всегда мучение из разряда “как мне в коротком и при этом понятном английском предложении выразить суть?”. С комментарием такой проблемы нет, потому что это просто естественный текст, не ограниченный по размеру.

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

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

Обычно для того, чтоб перейти к определению фукнции или вернуться обратно, достаточно нажать одну-две кнопки.

Для меня не проблема что это долго, скорее что у вас не вся логика перед глазами. То есть в каждый момент у вас перед глазами какая-то маленькая функция и вам нужно держать в голове какая там логика в месте её вызова. И наоборот, когда вы смотрите на место вызова, выам надо держать в голове что делает каждая маленькая функция. А если у вас перед глазами “простыня”, то вся логика как на ладони.

(Ну и понятное дело что если какая-то логика повторяется, то мы её вынесем в функцию в любом случае, DRY всё такое; но это я скорее на всякий случай уточняю).

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

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

fn my_big_func() {
{
  // do thing A
}
{
  // do thing B
}
{
  // do thing C
}
}

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

Тут зависит от того, какая задача стоит перед программистом.

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

Если же программисту не обязательно для своей задачи понимать функцию полностью, то ему наверное будет проще с подходом, где большая функция разбита на маленькие. Он увидит вызов doThingA() и решит “окей, тут делается вещь А, глубже читать не буду” (замечу, только при условии что имя у функции понятное и полностью отражает её содержимое!).

Вопрос в том, что чаще встречается - задачи для которых не нужен полный контекст функции или задачи для которых он нужен?

Я бы сказал что NPE хуже unwrap() в том смысле, что unwrap сразу виден и его, например, поиском по коду можно найти. А вот с местами, где разименовывается указатель всё не так просто, места где может возникнуть NPE не так очевидны

Или кандидат думает как мы, или он плохой.

Я если что интервьюера не защищаю, спорю только с вашей позицией.

Когда комментарий пишется по принципу “тут надо бы пояснить читателю”, то в нём смысла на порядок больше.

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

Конвенциальности в именование не вносятся.

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

не надо путать публичные библиотечные функции с кодовой базой внутреннего проекта

Я и ничего не путаю, я написал чёрным по белому “API какой-нибудь подсистемы внутри вашего продукта ничем не хуже API какой-нибудь библиотеки”. Вы так пишете, как-будто если код внутри нашего проекта, то просто у всех программистов в команде идеальное знание кода и что какая часть делает. Что если у нас один человек пишет модуль, а другой человек этим модулем пользуется? На API этого модуля всё равно не писать комментарии? Но ведь API модуля по сути публичное для второго программиста. Могу предвидеть контраргументы:

  • Но если люди в одной команде, то им проще спросить друг у друга что метод делает. - Но если что-то записано в комментарии в коде, то оно будет хранится в гите и будет доступно если кто-то новый в команду прийдёт. А то придётся опять у автора кода спрашивать “а как оно работает/как им правильно пользоваться” (если он ещё не уволился).

  • Во время разработки код часто меняется, поэтому писать комментарии бесполезно, они будут устаревать. - Это уже вопрос дисциплины в команде, такое надо на код ревью отлавливать.

Вот к чему такое чёрно-белое мышление? Представьте если бы в документации к стандартной библиотеке какого-нибудь языка (выберете на свой вкус) у функций не было никакого описание, только “удачно выбранное” название. Это был бы ад. Имел неудовольствие пользоваться библиотеками, где никакой документации на методах нет. Документация нужна, по крайней мере на внешнем API. И API какой-нибудь подсистемы внутри вашего продукта ничем не хуже API какой-нибудь библиотеки.

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

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

потому что комментарии бесполезны

Потому что их надо уметь писать, big news. Вы так пишете как-будто комментарии вообще всегда бесполезны, что не соответствует истине. Например в каком нибудь Си, вы предложите называть функцию так? “ReadFileReturnsZeroOnSuccessAndNegativeOneOnError(…)”. А то как же, комментарии писать нельзя!

Можно ли заранее проверить в коде, будет ошибка компиляции на println или нет

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

let string2 = &string1; // ссылка
// ...
let string2 = string1.clone(); // копия

Или можно создать свой SemiResult<T, S, E>

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

fallible_call()?;

это то же самое что

if let Err(e) = fallible_call() {
  return Err(e.into());
}

можно рассматривать это так. То есть возвращает ошибку выше, с возможность конвертации в ваш тип ошибки (если вы такое реализовали).

Есть ли офлайновый вариант работы, без подкачки всякого

Библиотеки можно подключать локально по указанию пути, можно разместить в вашем приватном репозитории и указать ссылку - как вам удобнее. Со стабильностью всё ок.

Какое-то подобие пользовательского деструктора?

Есть. Для типа можно имплементировать трейт Drop. Как раз ваш пример с файлом можно сделать. На нём построено многое в стандартной библиотеке - освобождение памяти, мьютексов и тд https://doc.rust-lang.org/std/ops/trait.Drop.html

зачем выделять макросы отдельно

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

в этом случае амперсанд можно было бы использовать вместо mut

Не понял ваш посыл. &x и &mut x это два разных типа ссылок. И это отдельные типы от x и mut x.

Как будто кто-то взял нормальный текст и рандомно накидал в него случайных значков типа !, ?, &, ’ и прочую mutь

На это часто жалуются новички, но…это ведь не понятно только для тех кто язык не знает, если ты язнык знаешь, то это для тебя не “случайные символы”. Я разве что могу понять претензию к тому что символ легко глазами пропустить. Я бы например предпочёл использовать not как в python вместо !. Ну и непонятна ваша претензия к ! и & - они в куче языком используются точно в том же смысле что и в расте (для отрицания и для взятия ссылки)

Зачем вообще нужно объявлять тип Result в качестве возвращаемого функцией?

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

Нет проблемы с unsafe - это легитимная часть языка и никто от неё избавляться не собирается. Не для всех вещей компилятор может доказать безопасность, в таких случаях используется unsafe. unsafe не делает “по цепочке” весь остальной код unsafe. unsafe код можно вызывать внутри safe кода. Обычно это небольшие небезопасные участки, обёрнутые в безопасное api.

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

зачем кому-то переписывать уже годами работающий крайне быстрый и отлаженный код на Си?

Согласен с вами в том что всё это “перепишем на раст” это сомнительные затеи, которые в большинстве случаев плохо обоснованы (это я говорю как фанат раста). Так сказать “не трогай то что работает”. Но, почему бы не писать новые программы на расте?

Ну а на счёт хайпа - раст уже много лет на плаву, так что какая-то ценность в нём всё таки есть. Думаю он с нами останется, может быть в своей нише (по типу как Го остался).

Ну так это сторонняя библиотека и вроде как у неё есть какие-то заковырки? В любом случае не часть стандартной поставки языка

Проблема II: Императивность

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

Проблема III: Некорректная или недостаточная обработка ошибок.

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

Проблема исключений № 1: Вы не знаете, что именно может упасть.

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

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

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

Вот любят в рунете взять и обосрать на ровном месте. Написать язвительную грубость вместо нормального комментария.
Не знаю как это объяснить - наследие совка?

Чтобы ещё раз ненавязчиво намекнуть, что бедную девочку угнетают?

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

Ну знаете, в том же c++ эту элементарную вещь тоже только в 17 издании добавили. Наверное не всё там так просто

Допустим у нас вредоносное вложение, которое должно вызывать чтение за пределами массива. C++ позволит прочитать данные за пределами массива, Rust не позволит.
Или например Rust сразу позволит отловить некоторые ошибки в логике (например, не позволит создать ситуацию где мы читаем из висячего указателя).

Нейроночная статья (привет m-dash) для рекламы курсов

Да, посмотрел по этому вопросу - раст сейчас не гарантирует copy elision, что иногда приводит к переполнению стека в случае больших структур (структура снача создаётся на стеке, потом только копируется на кучу). Иногда оптимизатор такую ситуацию исключает, но понятное дело опираться на оптимизатор это дело ненадёжное. Пока что единственный 100% способ создать большую структуру сразу на куче это в unsafe блоке самому выделить память и потом её инициализировать. Что-то вроде этого:

let my_box = unsafe {
            // выделяем
            let ptr = alloc::alloc(alloc::Layout::new::<MyType>()) as *mut MyType;
            
            // ...инициализируем

            // кладём в box
            Box::from_raw(ptr)
};

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

Information

Rating
6,035-th
Registered
Activity

Specialization

Бэкенд разработчик
Средний
Rust