Блог*
Open in Telegram
Блог со звёздочкой. Много репостов, немножко программирования. Небольшое прикольное комьюнити: @decltype_chat_ptr_t Автор: @insert_reference_here
Show more1 948
Subscribers
+124 hours
+237 days
+2430 days
Posts Archive
1 948
когда я встречаю людей, которые носят одноразовые маски по несколько дней, мне всегда хочется спросить: "у вас есть любимая одноразовая салфетка, которой вы вытираете рот?"
1 948
#ml
Правда, у меня есть стойкое подозрение, что такие хорошие результаты достигаются на запросах, для которых есть точно подходящие изображения из обучающей выборки
1 948
#prog #lisp #amazingopensource
LISP with GC in 436 bytes (достаточно, чтобы уместиться на загрузочном секторе)
1 948
Вообще, с майнкрафтом вечная история - приходят школьники, начинают учиться программировать и чето под майнкрафт кодят. Получается продсказуемо - всё забаговано и залаговано. Школьники постепенно учатся, вырастают и идут на нормальную работу (по собственному опыту говорю). В майнкрафт приходят новые.
В итоге вечно всё лагает и нихуя не работает. Никто не тестирует ничего под реальными нагрузками. Тесты вообще никто нигде не пишет, ни разу не видел.
1 948
Что пишут разработчики:
— Наш продукт основан на искусственном интеллекте!
Что слышу я:
— Когда наш софт сломается, мы не будем иметь ни малейшего понятия, как его чинить.
1 948
#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>;
1 948
#prog #rust #article
How not to learn Rust — статья с частыми ошибками новичков при изучении Rust
1 948
#prog #rust
Синтаксис для условной компиляции (
#[cfg(...)]) в Rust позволяет использовать условные выражения, но не в очень эргономичном виде: через all, any и not. Библиотека efg позволяет использовать более привычный синтаксис, похожий на то, что пишут в коде на самом Rust. Пример:
#[efg(feature "proc-macro" && !(target_arch "wasm32" && target_os "unknown"))]
extern crate proc_macro;
Кстати, написана Толяном.