Данный документ представляет собой краткое изложение некоторых аспектов, которые мы стремимся соблюдать в нашем стиле программирования. Некоторые из этих аспектов представляют собой позицию по определённым компромиссам. Описание будет стремиться сделать это ясным.
Основной мотивацией данного стиля программирования является повышение читаемости вашего кода.
Читаемость — это слово, которое скрывает несколько измерений:
Обнаружение того, что эти два аспекта различаются, не должно требовать значительных усилий. Стремитесь к проверяемости.
Перед отправкой кода на проверку пройдитесь по нему самостоятельно, чтобы избежать потери времени на проверке. Также, незначительные проблемы с стилем кода могут мешать обнаружению более глубоких проблем с кодом.
Как проверяющий, ваша первая миссия — проверка кода. Если вы обнаружите логическую ошибку, радуйтесь. Вы сделали отличную работу сегодня.
Ваша вторая цель — убедиться, что качество кода остаётся высоким.Вы можете высказывать "мелкие замечания": предложения о некоторых локальных аспектах кода, которые не имеют большого значения. Просто предварите ваш комментарий словом "nitpick:".
Вы также можете высказывать своё мнение/совет, зная, что оно не универсально.
Убедитесь, что вы делаете это ясным для проверяемого, что можно игнорировать комментарий.Не используйте риторические вопросы... Если вы на 95% уверены в чём-то, нет необходимости выражать это в виде вопроса.
Предпочтительнее использовать Я считаю, что это должно быть n+1
вместо Не должно ли это быть n+1?
.
Проблема с риторическими вопросами заключается в том, что когда у вас будет настоящий вопрос, проверяемый может переинтерпретировать его как утверждение.
Как проверяемый, если вы не привыкли к проверкам кода, это может казаться враждебным процессом. Отдохните. Нормально получать много комментариев на первых нескольких проверках кода.
Вы можете чувствовать, что комментарии необоснованны, постарайтесь не чувствовать раздражения. Если вы хотите обсудить это, лучшее место — чат, или, возможно, отправьте PR для модификации данного документа.
Но помните, что стоит выбирать свои битвы... Если вы считаете, что это не имеет большого значения, но это занимает 2 секунды для исправления, просто рассмотрите возможность выполнения предложенного проверяющим или этим стилем кодирования.
Когда он используется вместе с обработкой ошибок, стилистический подход Rust с цепочкой вызовов итератора может
вредить читаемости.
Использование традиционного цикла for
является приемлемым и рекомендуемым в этом случае.пример необходим
Имена функций и переменных ключевые для читаемости.
Хорошее имя функции часто достаточно для того, чтобы читатель мог сформировать разумные ожидания относительно того, что она делает.
Если это означает длинные имена, давайте будем использовать очень длинные имена.
Попытка соблюдать это правило имеет интересное побочное действие. Никто не любит вводить длинные имена функций. Это просто выглядит некрасиво. Но это часто симптомы плохо организованного кода, и это может помочь выявить возможности для рефакторинга.
пример необходим
Одним из невероятно мощных и простых инструментов для улучшения читаемости кода является введение объяснительных переменных.
Объяснительные переменные — это промежуточные переменные, которые не были строго необходимы, но их имена позволяют читателю понять их семантику.
пример необходим
По возможности избегайте повторного использования одного и того же имени переменной в функции. Это никогда не требуется, редко полезно и может вредить.
Rust поддерживает упрощение типов. Это замечательно. Вероятно, ваш редактор даже автоматически подсказывает типы переменных.
В некоторых случаях, однако, полезно для проверяющего указывать типы некоторых стратегически важных переменных.пример необходим
Мы предпочитаем ранние возвраты.
Вместо цепочки else
-условий мы предпочитаем изолировать
особые случаи в коротких if
-условиях, чтобы предотвратить вложенность.
пример необходим
Хорошая идея для помощи проверяющему в проверке кода — это
идентификация инвариантов и их выражение в виде debug_assert
.
Эти утверждения не будут включены в релизный бинарный файл и не будут замедлять выполнение.
пример необходим
Сообщения об ошибках и журналах следуют одному формату. Они должны быть краткими, с маленькой буквы (кроме правильных имён), и без окончательных знаков препинания.
Как общее правило, где это не вредит читаемости, сообщения журнала должны использовать tracing
структурированный логгинг вместо шаблонов.
Другими словами, предпочтительнее:
warn!(remaining=remaining_attempts, "trubulizor rpc plane retry failed")
вместо
warn!("trubulizor rpc plane retry failed ({remaining_attempts} attempts remaining)")
В частности, строка-суммаризация должна быть написана в третьем лице, единственном числе, настоящем времени.Отсутствие rustdoc в Quickwit или в приватных API допустимо. Отсутствие rustdoc для публичного API Tantivy недопустимо.
Обычно мы не ожидаем, что комментарии будут содержать какие-либо детали реализации. В определенной степени, это нормально, если пользователю придется посмотреть на код.
Когда смысл не ясен, комментарии должны передавать:
Встроенные комментарии в код могут быть очень полезны для помощи читателю понять оправдание сложного фрагмента кода.
необходим пример
Мы называем скрытым контрактом предусловие на аргументы, которое не контролируется их типами.
Иногда, скрытые контракты неизбежны.
Например, двоичный поиск требует, чтобы массив был отсортирован.
Когда это возможно, вы должны избегать наличия скрытых контрактов.
Чтобы избежать скрытых контрактов, вы должны рассмотреть:
Например, следующая функция не хороша, потому что скрывает контракт на том, что значения не пустые:
fn min(&self, values: &[usize]) -> usize {
let mut min_val = usize::MAX;
for val in values {
min_val = min_val.min(val)
}
min_val
}
Это можно сделать, изменяя прототип на Result
или Option
.Кроме того, хотя автор мог думать, что трюк usize::MAX
был хорошим, он может легко привести к проблемам. Лучше паниковать, чем вернуть неверный результат.
Лучший подход здесь, конечно, Option<usize>
как это делает Iterator::min
.
Еще один способ внутреннего контроля за выполнением контракта — переместить некоторую логику из вызывающего кода внутрь функции.
Например:
// Алгоритм требует, чтобы разделы были отсортированы по `end_time`
fn merge_candidates(splits: &mut Vec<SplitMetadata>) -> Vec<SplitMetadata>
Привлекательно полагаться на то, что разбиение Vec
всегда сортировано на стороне вызова и считать это скрытым соглашением.
Если это не слишком трудоемко, просто пересортировка данных внутри кандидатов на слияние — хорошая идея. Для вышеприведенной функции это дополнительная работа минимальна.
Кстати, знали ли вы, что сортировка стандартной библиотеки Rust вдохновлена timsort? Она будет работать за линейное время, если массив уже отсортирован...
При реализации функции с скрытым соглашением, если это не ухудшает общую производительность, добавьте утверждение (assert statement) в код для проверки этого соглашения. (Например, проверьте, что массив отсортирован).
*пример нужен
Тесты не должны соответствовать такому же качеству, как исходный код.Когда встречается ошибка, можно ввести тест, который кажется странным переопределением для конкретной проблемы. Комментарий должен добавлять ссылку на эту проблему.Юнит-тесты должны выполняться быстро, и если это возможно, они не должны выполнять ввод-вывод. Код должен быть структурирован таким образом, чтобы сделать юнит-тестирование возможным.
Некоторые из наших юнит-тестов не были бы признаны хорошими юнит-тестами в некоторых компаниях, и это нормально.
Вот спорные моменты:
Наши юнит-тесты не только для выявления регрессий. Они также проверяют правильность нашего кода.
Юнит-тесты не только тестируют публичный API. Сложный код часто вызывает полдюжины меньших функций.
Количество крайних случаев сложного кода может сделать тестирование всех крайних случаев затруднительным.
С другой стороны, меньшие функции могут быть тестируемы полностью.
По этой причине тестирование внутренних приватных функций на самом деле поощряется.
Идеально, юнит-тесты должны тестировать только одно, но если они это делают и это помогает охватить больше, это нормально.
Наконец, юнит-тесты не обязательно должны быть детерминированными. Мы очень любим proptests. Когда проводится proptesting, убедитесь, что вы сокращаете пространство исследования до максимума, чтобы получить максимум от этого.## async vs sync
Ваш async код должен блокироваться не более чем на 500 микросекунд.
Если вы не уверены, блокируется ли ваш код на 500 микросекунд, или если это не тривиальный вопрос, он должен выполняться через tokio::spawn_blocking
.
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )