#prog #java #kotlin #scala #article
Well-Typed Programs Can Go Wrong: A Study of Typing-Related Bugs in JVM Compilers
В этой статье авторы отмечают, что усилия в тестировании компиляторов в основном направлены на отлов некорректных оптимизаций, при этом мало кто целенаправленно занимается отловом багов в фронтендах. Авторы выбрали 320 багов, связанных с типами, среди багов компиляторов Java, Scala, Kotlin и Groovy, и разобрались с тем, как они себя проявляют, какие фичи языка используют и как они фиксятся. Вооружившись этим знанием, они написали генератор тестовых программ, который смог найти 28 багов, из них 12 были новыми (то есть для них исправлений не было).
А ещё авторы отличились тем, что нормально выложили материалы по исследованию.
Well-Typed Programs Can Go Wrong: A Study of Typing-Related Bugs in JVM Compilers
В этой статье авторы отмечают, что усилия в тестировании компиляторов в основном направлены на отлов некорректных оптимизаций, при этом мало кто целенаправленно занимается отловом багов в фронтендах. Авторы выбрали 320 багов, связанных с типами, среди багов компиляторов Java, Scala, Kotlin и Groovy, и разобрались с тем, как они себя проявляют, какие фичи языка используют и как они фиксятся. Вооружившись этим знанием, они написали генератор тестовых программ, который смог найти 28 багов, из них 12 были новыми (то есть для них исправлений не было).
А ещё авторы отличились тем, что нормально выложили материалы по исследованию.
www.semanticscholar.org
Well-typed programs can go wrong: a study of typing-related bugs in JVM compilers | Semantic Scholar
This study conducts the first empirical study for understanding and characterizing typing-related compiler bugs, and believes that it opens up a new research direction by driving future researchers to build appropriate methods and techniques for a more holistic…
#prog #rust #article
Stack-safety for free?
TL;DR:
Пока что решение ограничено в том плане, что оно не работает с мутабельными ссылками и взаимно-рекурсивными вызовами, но автор обещает рассказать в будущем о том, как можно обойти эти ограничения.
Также автор отдельно отмечает, что у него было чувство, что он изобрёл велосипед, но он не смог найти prior art для этого.
Stack-safety for free?
TL;DR:
//рекурсивная функция, использующая системный стек⬇️
fn triangular(n: u64) -> u64 {
if n == 0 {
0
} else {
n + triangular(n - 1)
}
}
//использует стек на кучеАвтор этой статьи столкнулся со случаем, когда ему потребовалось переделывать рекурсивное решение на итеративное из-за переполнения стека, и ему не понравилось, что он руками делает то, что вполне мог бы сделать компьютер автоматически. Немного подумав, он смог разработать решение, которое бы использовало генераторы (пока что всё ещё только на nightly) и которое требовало бы для работы достаточно прямолинейную трансформацию, чтобы её можно было сделать процедурными макросами.
fn triangular_safe(n: u64) -> u64 {
trampoline(|n| move |_| {
if n == 0 {
0
} else {
n + yield (n - 1)
}
})(n)
}
Пока что решение ограничено в том плане, что оно не работает с мутабельными ссылками и взаимно-рекурсивными вызовами, но автор обещает рассказать в будущем о том, как можно обойти эти ограничения.
Также автор отдельно отмечает, что у него было чувство, что он изобрёл велосипед, но он не смог найти prior art для этого.
@hurryabit's blog
Stack-safety for free?
I demonstrate how to (ab)use generators to transform any recursive function into an iterative one with nearly zero code changes.
Если вы когда-нибудь почувствуете себя глупым, то вспомните, что я разок опоздал на встречу с человеком из-за того, что перепутал станции Октябрьская и Октябрьское поле.
#rust
9 companies using Rust in production.
О некоторых из них я уже рассказывал, но в любом случае иметь сборник лишним не будет.
9 companies using Rust in production.
О некоторых из них я уже рассказывал, но в любом случае иметь сборник лишним не будет.
9 Companies That Use Rust in Production
Who uses Rust, and what are the benefits of choosing this programming language for your stack? Find out the answer in stories from 9 successful companies.
#prog #rust
Синтаксис для условной компиляции (
Синтаксис для условной компиляции (
#[cfg(...)]
) в Rust позволяет использовать условные выражения, но не в очень эргономичном виде: через all
, any
и not
. Библиотека efg позволяет использовать более привычный синтаксис, похожий на то, что пишут в коде на самом Rust. Пример:#[efg(feature "proc-macro" && !(target_arch "wasm32" && target_os "unknown"))]
extern crate proc_macro;
Кстати, написана Толяном.#prog #rust #article
Очередная статья про обработку ошибок в Rust, да. Но полезная: ставит под сомнения и разбирает распространённые мнения об ошибках в Rust. В частности, разбирается совет "anyhow для приложений, thiserror для библиотек".
Очередная статья про обработку ошибок в Rust, да. Но полезная: ставит под сомнения и разбирает распространённые мнения об ошибках в Rust. В частности, разбирается совет "anyhow для приложений, thiserror для библиотек".
Unwoundstack
Rust Error Handling
Some thoughts on how to handle errors in Rust
#prog #rust #моё
Пара слов о фантомных типах и авто-трейтах.
Иногда нам нужно включить нужный функционал, не привязанный к стейту. Примерно так:
Ответ очень прост: достаточно поменять определение поля
Есть, однако, и другой вариант использования фантомных типовых параметров: мы пишем тип данных, который как-то соотносится с неким параметром
Прямолинейным подходом в подобных случаях является использование
Для того, чтобы избежать этих проблем и иметь ограничения
Пара слов о фантомных типах и авто-трейтах.
Иногда нам нужно включить нужный функционал, не привязанный к стейту. Примерно так:
struct OptionA;
struct OptionB;
trait Param {
fn op();
}
impl Param for OptionA { ... }
impl Param for OptionB { ... }
struct Parametrised<P> {
data: Data,
_param: PhantomData<P>,
}
impl<P: Param> Parametrised<P> {
fn func(&mut self) {
self.data.process_foo();
P::op();
self.data.process_bar();
}
}
Вроде выглядит нормально. Однако тут есть подвох: авто-трейты, в отличие от дерайвов для большинства трейтов, смотрят не на типовые параметры, а на типы самих полей. В число авто-трейтов входят также Send
и Sync
. PhantomData (не) имеет те же реализации Send
/Sync
, что и тИповый параметр, которым параметризован. К чему это приводит? Да к тому, что если Parametrised<P>
будет использован в контексте, когда выполнение этих трейтов имеет значение, нам придётся вешать ограничение P: Send/P: Sync
, даже не смотря на то, что значения этих типов не используются вообще. Хуже, это придётся писать даже в том случае, если Data
реализует Send
/Sync
. Что мы можем с этим сделать?Ответ очень прост: достаточно поменять определение поля
_param
:struct Parametrised<P> {
data: Data,
_param: PhantomData<fn(P)>, // <--
}
Таким образом, мы всё ещё включаем тип P
в описание типа Parametrised
, но так как все функциональные указателя безусловно удовлетворяют Send
и Sync
, паразитные ограничения более не возникают.Есть, однако, и другой вариант использования фантомных типовых параметров: мы пишем тип данных, который как-то соотносится с неким параметром
T
, но не включает его напрямую. Использование PhantomData<T>
может быть неправильным в том смысле, что оно показывает, что наш тип логически владеет значением типа T
, что не обязательно является правдой. Более того, так как из-за лайфтаймов на типах в Rust есть отношение субтипизации, в полный рост встают проблемы вариантности.Прямолинейным подходом в подобных случаях является использование
PhantomData
, параметризованных *const T
и *mut T
. Этот подход опирается на тот факт, что *const T
ковариантен по T
, а *mut T
контравариантен по T
, и оба типа не требуют лайфтаймы. Однако тут легко упустить тот момент, что оба типа примитивных указателей безусловно не реализуют ни Send
, ни Sync
, что вкупе с протеканием автотрейтов через PhantomData
может привести к тем же паразитным ограничениям. Причём этот случай это даже хуже, поскольку требует написания реализаций Send
и Sync
вручную, а эти имплы очень легко могут стать неправильными при внесении изменений в код.Для того, чтобы избежать этих проблем и иметь ограничения
Send
/Sync
только на релевантные поля, мы опять воспользуемся тем фактом, что функциональные указатели реализуют Send
и Sync
безусловно, а так же тем фактом, что функциональные типы ковариантны по типу результата и контрвариантны по типу аргумента. Комбинируя эти типы с PhantomData
, мы можем получить нужную нам вариантность без головной боли, связанной с потокобезопасностью. Проиллюстрирую это псевдонимами типов:type Covariant<T> = PhantomData<fn() -> T>;
type Contravariant<T> = PhantomData<fn(T)>;
// ко- и контрвариантность — это про наложение ограничений,
// поэтому совмещение этих требований приводит к
// более строгому отношению: инвариантности (в смысле вариантности)
type Invariant<T> = PhantomData<fn(T) -> T>;
Telegram
Блог*
#prog #rust #article
Статья о #[derive(Clone)] и о том, почему стандартный дерайв-макрос ставит ограничение Clone на тИповые параметры, а не на типы полей.
Статья о #[derive(Clone)] и о том, почему стандартный дерайв-макрос ставит ограничение Clone на тИповые параметры, а не на типы полей.