Сижу вот изучаю Rust
Как вы, наверное, знаете, в C/C++ есть
И компилятор там всеми силами пытается его соптимайзить, фигачит
В Rust есть по сути тоже самое, но еще и со строками (
Короче - сам поиск примерно никак, используется линейный поиск, но оптимизируется сравнение строк.
Единственное, если размер строк разный, то он сделает jump table по их размеру (по сути, как с обычными числами)
А вот со сравнением строк уже интереснее. Да да, это скорее всего уже не сам раст, сколько llvm, но все равно интересно
1) Если строки <8 байт, то строка запишется как число, и компилятор просто прочитает строку как число и сделает сравнение
2) Если строки <16 байт, то сделает два сравнения (два числа 8 байт)
3) Если строки <32(?) байта, то фиганет SIMD сравнение (строки все еще числа)
4) Иначе, вызовет какую-то функцию сравнения длинных строк, которая, думаю, использует какие-то идеи из 1-3
Обидно конечно, что он не оптимайзит для длинных строк ничего. В моем случае (см картинку) очевидно, что можно понять, какая именно строка нужна по первому символу даже, и дальше сделать всего-лишь 1 проверку.
May be one day я решусь залезть в llvm...
Так что если вдруг кто-то решит переписать ejudge на раст, то придется и для него кодогенерировать бор для команд...
Как вы, наверное, знаете, в C/C++ есть
switch
, который работает для чиселИ компилятор там всеми силами пытается его соптимайзить, фигачит
jump table
какой-то или около тогоВ Rust есть по сути тоже самое, но еще и со строками (
match str
), и возник вопрос, а как оно там, собственно, оптимизируетсяКороче - сам поиск примерно никак, используется линейный поиск, но оптимизируется сравнение строк.
Единственное, если размер строк разный, то он сделает jump table по их размеру (по сути, как с обычными числами)
А вот со сравнением строк уже интереснее. Да да, это скорее всего уже не сам раст, сколько llvm, но все равно интересно
1) Если строки <8 байт, то строка запишется как число, и компилятор просто прочитает строку как число и сделает сравнение
2) Если строки <16 байт, то сделает два сравнения (два числа 8 байт)
3) Если строки <32(?) байта, то фиганет SIMD сравнение (строки все еще числа)
4) Иначе, вызовет какую-то функцию сравнения длинных строк, которая, думаю, использует какие-то идеи из 1-3
Обидно конечно, что он не оптимайзит для длинных строк ничего. В моем случае (см картинку) очевидно, что можно понять, какая именно строка нужна по первому символу даже, и дальше сделать всего-лишь 1 проверку.
May be one day я решусь залезть в llvm...
Так что если вдруг кто-то решит переписать ejudge на раст, то придется и для него кодогенерировать бор для команд...
godbolt.org
Compiler Explorer - Rust (rustc 1.55.0)
// Type your code here, or load an example.
pub fn some(s: &str) -> i32 {
match s {
"asomessmesomdsomesomesomesomessmesomdsomesomesome" => 0,
"bothrothrothrothrothrothrothrothrothrothrothrothr" => 1,
"cstufstufstufstufstufstuf…
pub fn some(s: &str) -> i32 {
match s {
"asomessmesomdsomesomesomesomessmesomdsomesomesome" => 0,
"bothrothrothrothrothrothrothrothrothrothrothrothr" => 1,
"cstufstufstufstufstufstuf…
👍1
В Go можно написать простой rate-limiter, который ограничивает нагрузку, двумя способами: перед запуском горутины, или внутри нее. Я вот не задумывался, что они отличаются довольно сильно в плане порядка обработки запросов
За подробностями прошу сюда: https://leviska.notion.site/3dbe7633fd5449ff99c485d147f2d420
За подробностями прошу сюда: https://leviska.notion.site/3dbe7633fd5449ff99c485d147f2d420
Лев's Notion on Notion
Рейтлимитер: внутри или снаружи | Notion
Начнем с простого: зачем нужен рейтлимитер?
👍1
Написал что-то похожее на kwargs, но статически типизированное и на расте. Не спрашивайте меня зачем. playground
Немного макросов, и я думаю, что можно сделать даже красиво
https://twitter.com/leviska0/status/1536502738237931520
Немного макросов, и я думаю, что можно сделать даже красиво
https://twitter.com/leviska0/status/1536502738237931520
👍3
Я все долго хотел написать пост про раст, и, наверное, когда-то его напишу, но недавно нашел это прекрасное видео, которое рассказывает про тот самый safety, что дает Rust, сравнивая куски кода с C++. Если вы все задавались вопросом "да кто такая безопасность в этом вашем расте", то видео очень хорошее
https://www.youtube.com/watch?v=IPmRDS0OSxM
https://www.youtube.com/watch?v=IPmRDS0OSxM
YouTube
A Firehose of Rust, for busy people who know some C++
Slides: https://jacko.io/firehose_of_rust
The slower version of this talk (2h32m): https://youtu.be/FSyfZVuD32Y
Contents:
0:00:00 introduction
0:04:03 references and mutable aliasing
0:06:54 reference lifetime examples
0:22:12 mutable aliasing examples
0:50:16…
The slower version of this talk (2h32m): https://youtu.be/FSyfZVuD32Y
Contents:
0:00:00 introduction
0:04:03 references and mutable aliasing
0:06:54 reference lifetime examples
0:22:12 mutable aliasing examples
0:50:16…
Где-то год назад я полностью осознал казалось бы очень простую мысль: "все программирование, все, с чем мы работаем, это все код. Код, который зачастую опенсорсный, куда ты можешь зайти, почитать его, и если вдруг нужно - поменять"
"Well, duh", скажете вы, но главная суть этой идеи в том, что менять опенсорсный код - это нормально и не так уж и сложно. Например, если вы выбираете какую-то библиотеку или инструмент для работы, и он вот чуть чуть не делает того, что вам надо, то у вас есть возможность просто сесть и доделать это. Не надо ныть, что "уу хорошая библиотека, но вот фичу не умеет", не надо искать другие менее популярные (и более багованые) аналоги, просто сядь и пофикси (лол)
Вчера вот я вышел на новый уровень какой-то: в Go есть хорошая ORM библиотека для БД gorm, и есть хороший инструмент для миграций goose, написанный на го, и в котором можно в том числе писать миграции на го. Но вторая штука не умеет работать из коробки с
Я пошел в репу и нашел PR, который за 10 строчек добавляет возможность удобно подрубить
Три часа дебаггинга спустя, я нашел забытый
Но почему я решил этим поделиться: для меня лично это был новый уровень, когда я фиксил даже не саму либу, а чей то пр этой либы. Т.е. мало того, что я решил не искать аналог и пофиксить уже хороший инструмент, так я не стал в очередной раз городить велосипед, а нашел уже чей-то другой велосипед и починил пару костылей в нем.
Вторая причина, почему я хотел этим поделиться, это попытаться завлечь вас делать так же. Я часто обсуждаю всякую прогу с друзьями и коллегами, и часто слышу как люди сталкиваются с похожими проблемами "вот есть штука, но она делает почти все, что надо", на что я отвечаю "ну просто пофикси лол))", и люди думают, что я шучу, мол "да зачем", "ой лезть еще туда", "я не смогу" и т.д., но при этом ни разу не пробовали так делать. За последние два года у меня уже накопилось несколько контрибьюшенов в опенсорс софт, и все эти контрибьюшены (не считая опенсорсных репозиториев с работы) были ровно такими же: есть хороший инструмент, но нужна какая-то мелочь, сел, разобрался, добавил эту мелочь, получил свои фичи и плюс в карму за помощь в разработке опенсорса. И скажу честно: в первые разы было очень тяжело, я действительно тратил много времени (несколько дней), чтобы просто разобраться в чужом коде, но это просто скилл, причем очень полезный, и я крайне рекомендую в следующий раз, когда от хорошего инструмента будет нужна еще какая-то мелочь, просто сесть и добавить ее
"Well, duh", скажете вы, но главная суть этой идеи в том, что менять опенсорсный код - это нормально и не так уж и сложно. Например, если вы выбираете какую-то библиотеку или инструмент для работы, и он вот чуть чуть не делает того, что вам надо, то у вас есть возможность просто сесть и доделать это. Не надо ныть, что "уу хорошая библиотека, но вот фичу не умеет", не надо искать другие менее популярные (и более багованые) аналоги, просто сядь и пофикси (лол)
Вчера вот я вышел на новый уровень какой-то: в Go есть хорошая ORM библиотека для БД gorm, и есть хороший инструмент для миграций goose, написанный на го, и в котором можно в том числе писать миграции на го. Но вторая штука не умеет работать из коробки с
gorm
, и в итоге по сути надо делать два соединения с бд и работать с gorm
через глобальную переменную, что не является проблемой, но блин некрасиво!Я пошел в репу и нашел PR, который за 10 строчек добавляет возможность удобно подрубить
gorm
. Но чел этот пр видимо не запускал, и когда я запустил его у себя локально, то у меня все ушло в вечный цикл и ничего не работало.Три часа дебаггинга спустя, я нашел забытый
return nil
, форкнул форк, пофиксил в нем багу, и теперь все хорошо работаетНо почему я решил этим поделиться: для меня лично это был новый уровень, когда я фиксил даже не саму либу, а чей то пр этой либы. Т.е. мало того, что я решил не искать аналог и пофиксить уже хороший инструмент, так я не стал в очередной раз городить велосипед, а нашел уже чей-то другой велосипед и починил пару костылей в нем.
Вторая причина, почему я хотел этим поделиться, это попытаться завлечь вас делать так же. Я часто обсуждаю всякую прогу с друзьями и коллегами, и часто слышу как люди сталкиваются с похожими проблемами "вот есть штука, но она делает почти все, что надо", на что я отвечаю "ну просто пофикси лол))", и люди думают, что я шучу, мол "да зачем", "ой лезть еще туда", "я не смогу" и т.д., но при этом ни разу не пробовали так делать. За последние два года у меня уже накопилось несколько контрибьюшенов в опенсорс софт, и все эти контрибьюшены (не считая опенсорсных репозиториев с работы) были ровно такими же: есть хороший инструмент, но нужна какая-то мелочь, сел, разобрался, добавил эту мелочь, получил свои фичи и плюс в карму за помощь в разработке опенсорса. И скажу честно: в первые разы было очень тяжело, я действительно тратил много времени (несколько дней), чтобы просто разобраться в чужом коде, но это просто скилл, причем очень полезный, и я крайне рекомендую в следующий раз, когда от хорошего инструмента будет нужна еще какая-то мелочь, просто сесть и добавить ее
👍7
Как выражать логику через систему типов, или пишем код, который не дает багать
https://leviska.notion.site/33f6445acb704440b57faae727123572
Я долго хотел написать этот пост, но все не мог найти хороший пример и не скатиться в "объяснение что такое Rust и почему он крута" на 30 страниц. И вроде бы получилось.
Если вы программируете на C++ и не понимаете вот этого хайпа вокруг Rust насчет "компилируется - значит работает" - пост для вас, с примерами на C++ и сравнением двух реализаций
https://leviska.notion.site/33f6445acb704440b57faae727123572
Я долго хотел написать этот пост, но все не мог найти хороший пример и не скатиться в "объяснение что такое Rust и почему он крута" на 30 страниц. И вроде бы получилось.
Если вы программируете на C++ и не понимаете вот этого хайпа вокруг Rust насчет "компилируется - значит работает" - пост для вас, с примерами на C++ и сравнением двух реализаций
Лев's Notion on Notion
Как выражать логику через систему типов | Notion
Или пишем код, который не дает багать
👍7
Еще когда я даже не учил Rust, но спрашивал у знающих знакомых "как работает XXX", меня часто не удовлетворял ответ: казалось, что для того, чтобы писать такой же эффективный код на
Возьмем
Плюсы говорят "вот тип, вы можете проверять, лежит ли в нем что-то, а можете не проверять, так как уже проверяли когда-то до этого, нам пофиг. Но если не проверите, то будет бобо"
Раст говорит "вот тип, вы или должны проверить, или должны явно указать, что вы умнее через
И во времена, когда я был ярым C++сером, мне казалось, что это будет как в плюсах, только неудобнее, потому что я даже не подозревал, что можно лучше.
Простой пример, как в C++ проверить, что что-то лежит внутри
Ногоспади спасибо разрабам раста конечно же никто так не пишет. Вместо этого, в Rust вы пытаетесь показать свою идею через систему типов. В данном случае вы хотите показать, что "вот тут я проверил
Но помимо этого, как я говорил, мы переложили ответственность следить за типами на компилятор. Поэтому, например, если кто-то удалит строчки с
Лично я называю это "писать код в стиле Rust". С одной стороны - по сути это просто синтаксический сахар, реальной разницы после компиляции (вроде бы) нет. Но с другой стороны, последний вариант читаемее и безопаснее. Иногда у меня возникает ощущение, что некоторые люди очень поверхностно изучают Rust и пытаются писать "как на плюсах", расстраиваются, и возвращаются обратно. Но если бы они приложили больше усилий, то вполне возможно, их мнение было бы совсем другим.
Rust
, как и на C++
, мне бы пришлось постоянно использовать unsafe
.Возьмем
Option/optional
:Плюсы говорят "вот тип, вы можете проверять, лежит ли в нем что-то, а можете не проверять, так как уже проверяли когда-то до этого, нам пофиг. Но если не проверите, то будет бобо"
Раст говорит "вот тип, вы или должны проверить, или должны явно указать, что вы умнее через
unsafe
"И во времена, когда я был ярым C++сером, мне казалось, что это будет как в плюсах, только неудобнее, потому что я даже не подозревал, что можно лучше.
Простой пример, как в C++ проверить, что что-то лежит внутри
optional
и получить значение:if (optional_value != nullopt) {Если это в лоб переписать на Rust, получим:
auto value = *optional_value; // используем "небезопасную" штуку без проверки, но мы же молодцы, мы проверили
std::cout << value << std::endl;
}
if optional_value.is_some() {И когда я получал ответ на свой вопрос, мне казалось что как-то так оно и работает и люди реально пишут такой код
let value = unsafe { optional_value.unwrap_unchecked() };
println!("{}", value);
}
Но
Option
, и 100% у меня лежит там значение" <=> вы бы хотели вместо Option<T>
иметь тип T
. И вместо того, чтобы держать это все в голове/комментариях (как в случае C++), мы выражаем это в типах, и перекладываем ответственность следить за типами на компилятор:if let Some(value) = optional_value {В первую очередь мне нравится то, что это тупо короче и читаемее (имхо). Мы буквально написали "если в
// typeof(value) == T
println!("{}", value);
}
Option
лежит что-то, то дай значение, иначе пропусти ветку" в одну строчку.Но помимо этого, как я говорил, мы переложили ответственность следить за типами на компилятор. Поэтому, например, если кто-то удалит строчки с
if
(или переместит/скопирует тело if
), то первые два варианта нормально скомпилируются, но последний - нет (т.к. у вас просто нет переменной value
)Лично я называю это "писать код в стиле Rust". С одной стороны - по сути это просто синтаксический сахар, реальной разницы после компиляции (вроде бы) нет. Но с другой стороны, последний вариант читаемее и безопаснее. Иногда у меня возникает ощущение, что некоторые люди очень поверхностно изучают Rust и пытаются писать "как на плюсах", расстраиваются, и возвращаются обратно. Но если бы они приложили больше усилий, то вполне возможно, их мнение было бы совсем другим.
👍6😁1🤔1
Panic! At the 0xC0D3
Еще когда я даже не учил Rust, но спрашивал у знающих знакомых "как работает XXX", меня часто не удовлетворял ответ: казалось, что для того, чтобы писать такой же эффективный код на Rust, как и на C++, мне бы пришлось постоянно использовать unsafe. Возьмем…
Когда выложил этот пост, скинули статью, которая довольно хорошо объясняет подход "писать код в стиле Rust"/"type driven design"
В статье все примеры кода на хаскеле, но параллельно с этим объясняется, что он делает, поэтому я не зная хаскеля в целом все смог понять
https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/
Не знаю, насколько много людей отсюда ее прочитают, но все таки решил поделиться
В статье все примеры кода на хаскеле, но параллельно с этим объясняется, что он делает, поэтому я не зная хаскеля в целом все смог понять
https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/
Не знаю, насколько много людей отсюда ее прочитают, но все таки решил поделиться
👍2
Panic! At the 0xC0D3
Когда выложил этот пост, скинули статью, которая довольно хорошо объясняет подход "писать код в стиле Rust"/"type driven design" В статье все примеры кода на хаскеле, но параллельно с этим объясняется, что он делает, поэтому я не зная хаскеля в целом все смог…
А поделиться я ей решил, потому что ее суть напомнила мне об одной мысли из какого-то древнего доклада, который я не могу откопать теперь(
Если верить моей памяти, то суть его была в том, что чуваку досталась какая-то легаси кодовая база с кучей багосов, и он решил поанализировать, какого рода баги там есть
И пришел к тому, что бОльшая часть самых неприятных ошибок, это... условия. Конкретно одинаковые по смыслу условия, разбросанные по коду
Ну например, вы проверяете, что строка начинается с какого-то префикса при обработке запроса, и в какой-то внутренней функции
Основная проблема в том, что код постоянно меняется, и вы, например, поменяете префикс в одном месте, но не в другом, хоп и получили баг
По сути, есть разные категории ошибок: проезды по памяти, гонки, логические ошибки и прочее. И многие ошибки мы умеем довольно быстро находить: (safe) Rust не даст вам скомпилировать код с проездом по памяти или гонкой, всякие санитайзеры помогут найти в C++
Но вот логические ошибки, это не поймать инструментами особо. Самый лучший инструмент - это написание тестов, что как мы все знаем, довольно часто игнорируется.
Еще после просмотра того доклада, я как-то начал относиться к любым "логическим"
И вот как раз этот подход из статьи выше, как мне кажется, помогает минимизировать количество таких ошибок: нужно пытаться выносить валидацию ("логические" проверки) в одно место, и после этого в системе типов как бы помечать этот объект "провалидированым", а в функциях и прочем уже использовать этот провалидированный тип
В статье в целом есть хорошие примеры, но я все равно приведу еще один: допустим, мы пишем какую-то библиотеку с математическими функциями, и многие функции могут принимать только положительные числа.
Если это целочисленные типы, то у нас уже(!) есть встроенное решение:
Что делать, если мы получаем число от пользователя (из json реквеста, ввод с клавиатуры, не важно)? Провалидировать его как можно раньше и засунуть в
Забавно то, что если вы начнете писать библиотеку в такой идеологии, то все функции такого рода будут принимать
Если верить моей памяти, то суть его была в том, что чуваку досталась какая-то легаси кодовая база с кучей багосов, и он решил поанализировать, какого рода баги там есть
И пришел к тому, что бОльшая часть самых неприятных ошибок, это... условия. Конкретно одинаковые по смыслу условия, разбросанные по коду
Ну например, вы проверяете, что строка начинается с какого-то префикса при обработке запроса, и в какой-то внутренней функции
Основная проблема в том, что код постоянно меняется, и вы, например, поменяете префикс в одном месте, но не в другом, хоп и получили баг
По сути, есть разные категории ошибок: проезды по памяти, гонки, логические ошибки и прочее. И многие ошибки мы умеем довольно быстро находить: (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
Panic! At the 0xC0D3
А поделиться я ей решил, потому что ее суть напомнила мне об одной мысли из какого-то древнего доклада, который я не могу откопать теперь( Если верить моей памяти, то суть его была в том, что чуваку досталась какая-то легаси кодовая база с кучей багосов, и…
По началу это может показаться странным. "Да никто так не делает", "это какой-то оверинжиниринг". Но по факту - это тот же
И на самом деле, нет, это используют, но больше в функциональных языках: вспомнить
"Но мой набор легаси библиотек не умеет с этим работать!" - возможно. Но это не мешает писать новые библиотеки в таком стиле.
И тут как раз хочется вернуться к
Есть ли перформанс оверхед от этого? It depends
Например, в случае
"Но я могу просто не проверять, и перенести это на плечи разработчика": да, но либо вам итак нужно будет проверять это (если это ввод от пользователя), и тогда разницы нет, будете вы проверять это в конструкторе, или внутри обработчика; либо вы на 100% уверены, что все ок, и тогда можно воспользоваться
В случае
Но это довольно простые примеры.
В реальности, зачастую это намного более сложные типы, которые показывают какие-то сложные инварианты. Самый банальный пример: парсить
Но в итоге, использовать библиотеки, написанные в
Вместо этого вам часто даже не нужна документация! Вы пишете
P. S. хотел кратко написать мысли по статье, проиграл
P. P. S. статью то все равно прочитайте!
uint
, но ufloat
, а первый используют повсеместно, чем этот тип хуже? И на самом деле, нет, это используют, но больше в функциональных языках: вспомнить
NonEmptyList
из статьи из хаскеля."Но мой набор легаси библиотек не умеет с этим работать!" - возможно. Но это не мешает писать новые библиотеки в таком стиле.
И тут как раз хочется вернуться к
Rust
: в стандартной библиотеке с давних времен есть "стандартные" решения подобных проблем: Option; Result; Box (unique_ptr), который не может быть null
- ровно по этой причине. И из-за того, что это было все (долгое?) время с языком, все библиотеки активно это используютЕсть ли перформанс оверхед от этого? It depends
Например, в случае
uf32
, вы наоборот можете выиграть в перформансе, не проверяя на отрицательные числа в каждой функции."Но я могу просто не проверять, и перенести это на плечи разработчика": да, но либо вам итак нужно будет проверять это (если это ввод от пользователя), и тогда разницы нет, будете вы проверять это в конструкторе, или внутри обработчика; либо вы на 100% уверены, что все ок, и тогда можно воспользоваться
unsafe
(который буквально показывает, что это небезопасно)В случае
NonEmptyList
(NonEmptyVec
) перформанс действительно может(!) быть хуже, потому что последовательная память, кеши и все такое.Но это довольно простые примеры.
В реальности, зачастую это намного более сложные типы, которые показывают какие-то сложные инварианты. Самый банальный пример: парсить
json
в структуру, и использовать ее везде, вместо использования абстрактного json::Value
, который бы пришлось валидировать в каждом месте (да да я смотрю на тебя python
)Но в итоге, использовать библиотеки, написанные в
type driver design
намного намного приятнее и проще. Одни флешбеки с numpy
функций после курса МО, в которых нужно заглянуть в документацию, чтобы увидеть value: int/float/array/list/object/ndarray
, передать туда какой-нибудь pandas.Series
и получить ошибкой в рантайме спустя 10 минут вычислений, потому что "а этого нет в списке извините", заставляют вздрогнуть.Вместо этого вам часто даже не нужна документация! Вы пишете
foo(bar)
, и либо оно скомпилировалось и скорее всего работает, либо нет. Помните слова "компилируется - значит работает" про Rust
, да? :)P. S. хотел кратко написать мысли по статье, проиграл
P. P. S. статью то все равно прочитайте!
👍5
Я не могу этим не поделиться
Недавно я делал внутреннюю тулзу, для которой хотелось запускать питон код
Я решил попробовать сделать это не с помощью процессов, а запускать питон прям в этом же процессе (язык для скриптов же все дела) (плюс можно было шерить память за очень быстро)
Для раста есть прекрасная либа под названием pyo3 (а еще там же есть биндинги numpy)
И мне надо было сделать
Ну я и пишу какой-то код аля
1. Компилятор(!) знал и понял, что
А именно, он мне сказал:
"PyDateTime" нельзя сувать в
И если я хочу быстро, то мне нужно использовать его.
И УЗНАЛ Я ОБ ЭТОМ НЕ ИЗ РАНТАЙМ ОШИБКИ, НЕ ИЗ ГУГЛА, А ОТ КОМПИЛЯТОРА
АААААААА
К слову, часто раст рекламируют как "memory safety", но на самом деле его система типов позволяет делать намного больше, как, например, тут, правильно описывать апи вообще другого языка.
Помимо этого, мой экспириенс с питоном в расте был мега приятный. Я не знал, можно ли в структурах, которые будут экспортироваться в питон, использовать обычные типы из раста (условно, нужно ли использовать
И такого рода вещей было очень много. Я глядел совсем чутка на то, как это делается в плюсах, и понял, что там просто UB на UB (C api все таки), и был в шоке, наскольно приятно это делается тут.
В общем, это довольно хороший пример большой библиотеки с кучей сложных вещей, которая при этом позволяет добиваться того самого волшебного момента "компилируется - значит работает"
Недавно я делал внутреннюю тулзу, для которой хотелось запускать питон код
Я решил попробовать сделать это не с помощью процессов, а запускать питон прям в этом же процессе (язык для скриптов же все дела) (плюс можно было шерить память за очень быстро)
Для раста есть прекрасная либа под названием pyo3 (а еще там же есть биндинги numpy)
И мне надо было сделать
datetime numpy array
(массив дат)Ну я и пишу какой-то код аля
PyArray1::<PyDateTime>::from_iter(dates...);И что бы вы думали
1. Компилятор(!) знал и понял, что
PyDateTime
нельзя пихать в numpy array
2. Но он не только сказал, что нельзя, но написал мне ошибку со скринаА именно, он мне сказал:
"PyDateTime" нельзя сувать в
numpy array
, но есть несколько других типов, которые можно, например numpy::DateTime
Оказывается, что у нампая свой тип дат (что логично, вместо всей структуры с полями типа year, month, day
и т.д., там просто хранится timestamp
)И если я хочу быстро, то мне нужно использовать его.
И УЗНАЛ Я ОБ ЭТОМ НЕ ИЗ РАНТАЙМ ОШИБКИ, НЕ ИЗ ГУГЛА, А ОТ КОМПИЛЯТОРА
АААААААА
К слову, часто раст рекламируют как "memory safety", но на самом деле его система типов позволяет делать намного больше, как, например, тут, правильно описывать апи вообще другого языка.
Помимо этого, мой экспириенс с питоном в расте был мега приятный. Я не знал, можно ли в структурах, которые будут экспортироваться в питон, использовать обычные типы из раста (условно, нужно ли использовать
PyString
, или можно String
), и вместо того, чтобы гуглить, безопасно ли это, можно ли так, я просто писал такой код, и он компилировался, и я был уверен, что так можно (и да, все ок)И такого рода вещей было очень много. Я глядел совсем чутка на то, как это делается в плюсах, и понял, что там просто UB на UB (C api все таки), и был в шоке, наскольно приятно это делается тут.
В общем, это довольно хороший пример большой библиотеки с кучей сложных вещей, которая при этом позволяет добиваться того самого волшебного момента "компилируется - значит работает"
👍10😁1🤔1
Очень хороший пост-статья про то, как работает
Крайне интересное чтиво
https://blog.burntsushi.net/regex-internals/
#статья
regex
изнутри, и какие проблемы встречаются при реализацииКрайне интересное чтиво
https://blog.burntsushi.net/regex-internals/
#статья
burntsushi.net
Regex engine internals as a library - Andrew Gallant's Blog
I blog mostly about my own programming projects.
👍3
https://world.hey.com/dhh/the-price-of-managed-cloud-services-4f33d67e
Хороший маленький пример сравнения cloud и bare metal с ценами (bare metal победил).
Одна из существенных проблем - очень тяжело оптимизировать стоимость клауда
Хороший маленький пример сравнения cloud и bare metal с ценами (bare metal победил).
Одна из существенных проблем - очень тяжело оптимизировать стоимость клауда
Hey
The price of managed cloud services
One of the common objections to our cloud exit has been that we shouldn't have expected good outcomes from a lift'n'shift operation. That the real value of the cloud is in managed services and new architectures, not just running the same software on rented…
👍2
Чем больше читаешь про hardware, тем меньше доверяешь компьютерам (с)
https://xuanwo.io/2023/04-rust-std-fs-slower-than-python/
https://xuanwo.io/2023/04-rust-std-fs-slower-than-python/
xuanwo.io
Rust std fs slower than Python!? No, it's hardware!
Achieving Data Freedom Through Open Source and Rust