PANICCODE Telegram 18
Panic! At the 0xC0D3
Когда выложил этот пост, скинули статью, которая довольно хорошо объясняет подход "писать код в стиле Rust"/"type driven design" В статье все примеры кода на хаскеле, но параллельно с этим объясняется, что он делает, поэтому я не зная хаскеля в целом все смог…
А поделиться я ей решил, потому что ее суть напомнила мне об одной мысли из какого-то древнего доклада, который я не могу откопать теперь(
Если верить моей памяти, то суть его была в том, что чуваку досталась какая-то легаси кодовая база с кучей багосов, и он решил поанализировать, какого рода баги там есть
И пришел к тому, что бОльшая часть самых неприятных ошибок, это... условия. Конкретно одинаковые по смыслу условия, разбросанные по коду
Ну например, вы проверяете, что строка начинается с какого-то префикса при обработке запроса, и в какой-то внутренней функции
Основная проблема в том, что код постоянно меняется, и вы, например, поменяете префикс в одном месте, но не в другом, хоп и получили баг

По сути, есть разные категории ошибок: проезды по памяти, гонки, логические ошибки и прочее. И многие ошибки мы умеем довольно быстро находить: (safe) Rust не даст вам скомпилировать код с проездом по памяти или гонкой, всякие санитайзеры помогут найти в C++
Но вот логические ошибки, это не поймать инструментами особо. Самый лучший инструмент - это написание тестов, что как мы все знаем, довольно часто игнорируется.

Еще после просмотра того доклада, я как-то начал относиться к любым "логическим" ifам с осторожностью: по сути, каждый из них может быть причиной бага.
И вот как раз этот подход из статьи выше, как мне кажется, помогает минимизировать количество таких ошибок: нужно пытаться выносить валидацию ("логические" проверки) в одно место, и после этого в системе типов как бы помечать этот объект "провалидированым", а в функциях и прочем уже использовать этот провалидированный тип

В статье в целом есть хорошие примеры, но я все равно приведу еще один: допустим, мы пишем какую-то библиотеку с математическими функциями, и многие функции могут принимать только положительные числа.
Если это целочисленные типы, то у нас уже(!) есть встроенное решение: unsized int и его вариации:
fn sqrt(val: u32) -> u32
Все, нам не нужны никакие проверки внутри функции, система типов гарантирует это. И я думаю, что вы согласитесь, что да, это разумное решение и у него нет особо минусов.
Что делать, если мы получаем число от пользователя (из json реквеста, ввод с клавиатуры, не важно)? Провалидировать его как можно раньше и засунуть в unsigned int тип, а дальше работать только с ним.
Забавно то, что если вы начнете писать библиотеку в такой идеологии, то все функции такого рода будут принимать uint, а следовательно они как бы будут заставлять вас провалидировать значение как можно раньше:
fn sqrt(val: u32) -> u32 // базовая функция
fn round_sqrt(val: u32) -> u32 // какая-то функция, которая использует первую
fn handle(val: String) { // наш обработчик
let res = round_sqrt(val???); // хотим вызвать "сложную" функцию, но она сразу требует провалидированный тип
}

Но что если мы хотим еще и работать с числами с плавающей точкой? В большинстве языков нет встроенного типа для этого. Дак давайте сделаем свой!
pub struct uf32 {
val: f32, // приватное
}
fn new(val: f32) -> uf32 {
if val < 0.0 {
panic!("expected positive value"); // или, еще лучше, можно возвращать ошибку
}
return uf32{ val };
}
unsafe fn new_unchecked(val: f32) -> uf32 { // явно помечаем, что это небезопасно
return uf32{ val };
}
// арифм операции и прочее
По сути, это тоже самое, что и uint: чтобы его получить, (по хорошему) нужно проверить, что значение действительно соответсвует типу. И после этого, можно его использовать уже без всяких проверок
fn sqrt(val: uf32) -> uf32; // без проверок
👍3



tgoop.com/paniccode/18
Create:
Last Update:

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

По сути, есть разные категории ошибок: проезды по памяти, гонки, логические ошибки и прочее. И многие ошибки мы умеем довольно быстро находить: (safe) Rust не даст вам скомпилировать код с проездом по памяти или гонкой, всякие санитайзеры помогут найти в C++
Но вот логические ошибки, это не поймать инструментами особо. Самый лучший инструмент - это написание тестов, что как мы все знаем, довольно часто игнорируется.

Еще после просмотра того доклада, я как-то начал относиться к любым "логическим" ifам с осторожностью: по сути, каждый из них может быть причиной бага.
И вот как раз этот подход из статьи выше, как мне кажется, помогает минимизировать количество таких ошибок: нужно пытаться выносить валидацию ("логические" проверки) в одно место, и после этого в системе типов как бы помечать этот объект "провалидированым", а в функциях и прочем уже использовать этот провалидированный тип

В статье в целом есть хорошие примеры, но я все равно приведу еще один: допустим, мы пишем какую-то библиотеку с математическими функциями, и многие функции могут принимать только положительные числа.
Если это целочисленные типы, то у нас уже(!) есть встроенное решение: unsized int и его вариации:
fn sqrt(val: u32) -> u32
Все, нам не нужны никакие проверки внутри функции, система типов гарантирует это. И я думаю, что вы согласитесь, что да, это разумное решение и у него нет особо минусов.
Что делать, если мы получаем число от пользователя (из json реквеста, ввод с клавиатуры, не важно)? Провалидировать его как можно раньше и засунуть в unsigned int тип, а дальше работать только с ним.
Забавно то, что если вы начнете писать библиотеку в такой идеологии, то все функции такого рода будут принимать uint, а следовательно они как бы будут заставлять вас провалидировать значение как можно раньше:
fn sqrt(val: u32) -> u32 // базовая функция

fn round_sqrt(val: u32) -> u32 // какая-то функция, которая использует первую
fn handle(val: String) { // наш обработчик
let res = round_sqrt(val???); // хотим вызвать "сложную" функцию, но она сразу требует провалидированный тип
}

Но что если мы хотим еще и работать с числами с плавающей точкой? В большинстве языков нет встроенного типа для этого. Дак давайте сделаем свой!
pub struct uf32 {
val: f32, // приватное
}
fn new(val: f32) -> uf32 {
if val < 0.0 {
panic!("expected positive value"); // или, еще лучше, можно возвращать ошибку
}
return uf32{ val };
}
unsafe fn new_unchecked(val: f32) -> uf32 { // явно помечаем, что это небезопасно
return uf32{ val };
}
// арифм операции и прочее
По сути, это тоже самое, что и uint: чтобы его получить, (по хорошему) нужно проверить, что значение действительно соответсвует типу. И после этого, можно его использовать уже без всяких проверок
fn sqrt(val: uf32) -> uf32; // без проверок

BY Panic! At the 0xC0D3


Share with your friend now:
tgoop.com/paniccode/18

View MORE
Open in Telegram


Telegram News

Date: |

Telegram has announced a number of measures aiming to tackle the spread of disinformation through its platform in Brazil. These features are part of an agreement between the platform and the country's authorities ahead of the elections in October. The best encrypted messaging apps How to create a business channel on Telegram? (Tutorial) How to Create a Private or Public Channel on Telegram? Other crimes that the SUCK Channel incited under Ng’s watch included using corrosive chemicals to make explosives and causing grievous bodily harm with intent. The court also found Ng responsible for calling on people to assist protesters who clashed violently with police at several universities in November 2019.
from us


Telegram Panic! At the 0xC0D3
FROM American