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: |

How to create a business channel on Telegram? (Tutorial) During a meeting with the president of the Supreme Electoral Court (TSE) on June 6, Telegram's Vice President Ilya Perekopsky announced the initiatives. According to the executive, Brazil is the first country in the world where Telegram is introducing the features, which could be expanded to other countries facing threats to democracy through the dissemination of false content. Step-by-step tutorial on desktop: Just at this time, Bitcoin and the broader crypto market have dropped to new 2022 lows. The Bitcoin price has tanked 10 percent dropping to $20,000. On the other hand, the altcoin space is witnessing even more brutal correction. Bitcoin has dropped nearly 60 percent year-to-date and more than 70 percent since its all-time high in November 2021. A Telegram channel is used for various purposes, from sharing helpful content to implementing a business strategy. In addition, you can use your channel to build and improve your company image, boost your sales, make profits, enhance customer loyalty, and more.
from us


Telegram Panic! At the 0xC0D3
FROM American