1.84K subscribers
3.28K photos
130 videos
15 files
3.55K links
Блог со звёздочкой.

Много репостов, немножко программирования.

Небольшое прикольное комьюнити: @decltype_chat_ptr_t
Автор: @insert_reference_here
Download Telegram
«У России три пути: вебкам, закладки и айти»

> OnlyFans приостанавливает работу аккаунтов из России
> полиция Германии ликвидирует Гидру
> Интел полностью останавливает работу в России

У России… нет путей?
🤔20👍9😢62
Forwarded from partially unsupervised
Существует известная проблема из области социологии: как получить более или менее честные данные в количественном исследовании, если респонденты не хотят/боятся/стыдятся отвечать честно.

И вот случайно наткнулся на простой и изящный подход в духе differential privacy. TL;DR - формируем список из N утверждений, делим респондентов на две группы, одной показываем все N утверждений, другой - все, кроме того самого чувствительного. Вопрос формулируется как "с каким количеством утверждений (неважно, каких именно) вы согласны", и по разнице между группами легко вывести истинную поддержку.

Должно подойти не только для опросов про войну, но и для user research.
🔥11
Блог*
#rust В сорцах rustc банят числа.
Ввиду последних событий, вероятно, будут банить числа 86 и 90
👍1😁1
Задал ребенку блестящее (не могу кривить душой!) задание.

Мы решили (так как беседуем с ней раз в неделю уже третий год), что пора обсуждать литературу. Решили начать с мифа. Ученица начиталась терминологии, объяснила мне, что такое космогонический миф, а что такое онтологический. Было очень здорово, хотя слова эти по первости она произносила по слогам. Мы приводили друг другу примеры, пересказывали мифы разных народов и оба собой глубоко гордились.

И тут ученица задала блестящий вопрос — а выросли ли мы уже, все человечество, из мифов? Я задумался и предположил, что нет. В качестве примера я привел детскую мифологию, объясняющую мир. Например, мне в деревенском детстве объясняли, что убивать пауков нельзя. Если убьешь паука, то Бог плачет и идет дождь. Девочка очень обрадовалась и стала толковать. Пауки полезны, они очищают дом от насекомых, их нельзя убивать; но надо объяснить, почему. Я к этому добавил, что рассказывали мне это про пауков-крестовиков, у которых на спинке буквально крест — отсюда и мотив Бога. Ученица в качестве своего детского мифа привела историю про аиста, приносящего детей.

И тут у меня в голове щелкнуло. Я поразмыслил и предложил ей провести исследование. Буквально расспросить одноклассников о мифах, в которые они верили в детстве и с помощью которых им объяснили мир. А еще лучше — расспросить родителей о том, во что они верили в детстве. Ученица прямо самостоятельно и без напоминания записала все в тетрадочку и сообщила, что это очень остроумное задание. Я не мог не согласиться и решил ей польстить.

— Вообще-то это можно даже назвать антропологическим исследованием! — важно сказал я.

Девочка округлила глаза и тут же записала такое важное и звучное выражение.

Буду держать вас в курсе.
👍4
Forwarded from Макс Силинг
cat вступил в профсоюз и его теперь нельзя уволить с его работы на первом месте в пайплайне
🔥7
#prog #rust #моё

Пусть у нас есть слайс из вот таких структур:

struct Data {
name: String,
whatever: u32,
bytes: Vec<u8>,
}

, и нам надо отсортировать слайс по полю name. Казалось бы, плёвое дело:

arr.sort_by_key(|d| &d.name);

Но компилятор ругается:

error: lifetime may not live long enough
--> src/lib.rs:8:21
|
8 | arr.sort_by_key(|d| &d.name);
| -- ^^^^^^^ returning this value requires that
'1 must outlive '2
| ||
| |return type of closure is &'2 String
| has type &'1 Data

В чём дело? Посмотрим на сигнатуру sort_by_key:

fn sort_by_key<K, F>(&mut self, f: F) where
F: FnMut(&T) -> K,
K: Ord,

Обратите внимание, параметр K не параметризован никакими временами жизни и потому считается независимым от переданной ссылки на T. Очевидно, в нашем случае это не так: ссылка на поле структуры не может жить дольше ссылки на структуру целиком.

Что делать? Можно, конечно, клонировать строку, но делать аллокацию в памяти только ради того, чтобы сравнить данные и тут же их деаллоцировать — это расточительно. Проще использовать sort_by, который требует функцию, возвращающую Ordering:

arr.sort_by(|a, b| a.name.cmp(&b.name));

Окей, это работает, но выглядит уже не очень. Нам приходится повторять себя, да и идея "сравнить по name" уже не так хорошо читается. А если нам потребуется сравнивать сначала по name, а потом по bytes?

arr.sort_by(|a, b| (&a.name, &a.bytes).cmp(&(&b.name, &b.bytes)));

Ох, выглядит так себе, от обилия скобочек немного рябит в глазах. А если переписать на методах Ordering?

arr.sort_by(|a, b| a.name.cmp(&b.name).then_with(|| a.bytes.cmp(&b.bytes)));

Лучше особо не стало. И, кстати, что там, что там велик соблазн скопипастить выражения для a и заменить на b — великолепный способ, чтобы забыть в каком-то месте поменять. Мы можем сделать лучше! Упрячем sort_by в одном месте и выставим интерфейс, который принимает функцию для вынимания ключа:

trait SliceExt<T> {
fn sort_by_ref_key<K, F>(&mut self, f: F)
where
F: FnMut(&T) -> &K,
K: Ord + ?Sized; //
почему "?Sized"?
}

impl<T> SliceExt<T> for [T] {
fn sort_by_ref_key<K, F>(&mut self, mut f: F)
where
F: FnMut(&T) -> &K,
K: Ord + ?Sized,
{
self.sort_by(|a, b| f(a).cmp(&f(b)));
}
}

Теперь мы можем красиво отсортировать по имени:

arr.sort_by_ref_key(|d| &d.name);

Не менее красиво мы можем отсортировать по имени и по байтам... А нет, погодите, не можем — это потребует возвращать пару ссылок, а кортеж из ссылок ссылкой не является ауф. Так что до новых встреч.
👍2
...Или мы можем подрубить nightly и абстрагироваться от конкретной разновидности заимствующих ключей при помощи GAT-ов!

#![feature(generic_associated_types)]

Так как непосредственно конструктор типа мы передать не можем, мы передадим некий маркерный тип, к которому при помощи трейта прицепим ассоциированными типом нужный нам тип ключа:

trait BorrowedKeyFamily {
type Key<'a>: Ord + 'a;
}

Теперь нам нужен этот самый маркерный тип написать и связать его с кортежем ссылок:

struct PairRef<T, U: ?Sized>(std::marker::PhantomData<(T, U)>);

impl<T, U> BorrowedKeyFamily for PairRef<T, U>
where
T: Ord + 'static,
U: Ord + 'static + ?Sized,
{
type Key<'a> = (&'a T, &'a U);
}

Как обычно, типовые сигнатуры начинают занимать больше места, чем сам код. Что ж, используем всё это для нового метода в SliceExt:

trait SliceExt<T> {
// ...

fn sort_by_borrowed_key<K, F>(&mut self, f: F)
where
F: FnMut(&T) -> K::Key<'_>,
K: BorrowedKeyFamily;
}

impl<T> SliceExt<T> for [T] {
// ...

fn sort_by_borrowed_key<K, F>(&mut self, mut f: F)
where
F: FnMut(&T) -> K::Key<'_>,
K: BorrowedKeyFamily
{
self.sort_by(|a, b| f(a).cmp(&f(b)));
}
}

Кстати, благодаря анонимному лайфтайму нам не пришлось писать F: for<'a> FnMut(&'a T) -> K::Key<'a> (и, признаться честно, я удивлён, что для GAT это работает). Теперь напишем сортировку с только что сделанным методом:

arr.sort_by_borrowed_key(|d| (&d.name, &d.bytes));

Всем спасибо, все сво...

error[E0282]: type annotations needed
--> src/lib.rs:12:9
|
12 | arr.sort_by_borrowed_key(|d| (&d.name, &d.bytes));
| ^^^^^^^^^^^^^^^^^^^^ cannot infer type for type parameter `K` declared on the associated function `sort_by_borrowed_key`

А, компилятор не может понять, что за маркерный тип тут используется. Что ж, неприятно, но вполне логично — мы теоретически можем написать ещё один тип и реализовать для него BorrowedKeyFamily с парой ссылок (вот, кстати, место, где пригодился бы аналог FunctionalDependencies из GHC). Придётся воткнуть турборыбу:

arr.sort_by_borrowed_key::<PairRef<_, _>, _>(|d| (&d.name, &d.bytes));

Выглядит уже не так клёво. Вдобавок, у нашего кода есть одно досадное и неочевидное ограничение. Посмотрим ещё раз на ограничения на тип Key:

type Key<'a>: Ord + 'a;

То есть что бы ни лежало в Key<'a>, оно должно жить как минимум 'a. А у нас 'a может быть произвольным. Для T и U в составе пары это фактически выливается в ограничение for<'a> 'a, что эквивалентно 'static по определению. На практике это означает, что мы не сможем класть в ключ строковые слайсы, что резко ограничивает применимость подхода.

...Ну, зато мы выпендрились GAT-ами. Как всегда, весь код в гисте.
👍6
Forwarded from Санечка Ъысь
👍6🔥41
fn with_order<'o>(order: &'o Order) -> impl (for<'a> Fn(&'a str) -> (impl Iterator<Item = usize> + 'a + 'o)) + 'o

Real Rust code written by real programmers

(which doesn't actually work)
А для того, чтобы оно всё-таки работало, нужно, чтобы реализовали impl Fn() -> impl Trait in return position (привет, Вафель) и, желательно, синтаксис для явного написания замыкания с полиморфизмом высшего ранга.

И то, кстати, не факт, что сработает, поскольку в силу того, что возвращаемое замыкание содержит &'o Order, это замыкание, вообще говоря, должно принимать ссылку на строковой слайс не с произвольным временем жизни, а живущую не менее, чем 'o, а ограничение на времена жизни в higher-ranked trait bounds не работают даже для fn pointers.
#prog #tips

Не используйте для подписи коммитов на Github GPG-ключи, у которых указан e-mail, но не имя — Github почему-то считает, что к этим ключам не прикреплены e-mail (The email in this signature doesn’t match the committer email) и потому отказывается верифицировать коммиты, подписанные этим ключом.
Forwarded from Санечка Ъысь
🔥41
#prog #rust #rustreleasenotes

Вышла версия Rust 1.60.0!

(да, да, я в курсе, что это было неделю назад)

Как обычно, я разбираю лишь то, что интересно лично мне, за полными заметками идите в репу раста.

▪️Помните, инкрементальную компиляцию отключили по умолчанию, потому что она была сломана? Так вот, её починили и включили обратно.
(анекдот-про-козу.txt)

▪️В этот раз изменений в самом языке совсем мало — они в основном в библиотеке и тулинге — но те, что есть, достаточно важны для писателей библиотек. Теперь возможно делать условную компиляцию по стратегии реализации паники и по наличию на целевой платформе атомиков конкретного размера — последнее, вероятно, связано, связано с усилиями в рамках проекта Rust for Linux. Писать корректный и переносимый код стало немного проще — и, кстати, первое изменение теперь позволяет сделать ошибку компиляции для кода, который полагается на то, что паника реализовывается через раскрутку стека, для обеспечения корректности (да, substrate, я смотрю на тебя).

▪️Важное изменение касательно cargo-манифестов. До нынешней версии наличие опциональной зависимости автоматически создавало feature с таким же именем. Это временами не очень удобно, поскольку вызывает конфликты имён из глобального пространства имён крейтов с локальным пространством имён для фичей. Более того, использование фичи из зависимости автоматически подключало эту зависимость, что несколько уменьшало весь смысл её возможной опциональности.

Чтобы покончить с этим безобразием, Cargo.toml теперь поддерживает два новых синтаксиса для списка фичей (вместе с новой семантикой, разумеется). Первый — это префикс dep:. Его использование означает подключение зависимости для данной фичи без создания одноимённой фичи для крейта. Второй — это суффикс ? для имени зависимости. Строка dependency?/feature_name означает, что используется фича feature_name из dependency — но только в том случае, если эта зависимость реально используется.

Вот пример из официального блогопоста:

[dependencies]
serde = { version = "1.0.133", optional = true }
rgb = { version = "0.8.25", optional = true }

[features]
serde = ["dep:serde", "rgb?/serde"]

Включение фичи serde для определяемого крейта в данном примере также включает фичу serde для зависимости rgb, но только в том случае, если эта зависимость активирована явно посредством чего-то другого.

▪️Компилятор теперь по умолчанию использует полный формат трассировки стека при паниках, если он не задан явно. Дебажить ошибки в компиляторе (особенно гейзенбаги) стало немного проще.

▪️Стабилизировали abs_diff для примитивных численных типов. Наконец-то, джва года ждал.

▪️Instant больше не использует хаки для обеспечения монотонности на платформах, на которых декларируемо монотонные часы не являются таковыми. Это позволяет избавиться от оверхеда на синхронизацию на таких платформах. Взамен теперь методы, которые ранее паниковали на underflow, вместо этого используют насыщающее поведение (т. е. заменяют отрицательную разницу на ноль). Да, это означает, что некоторые ошибки в логике программы теперь будут проходить незамеченными. Instant::checked_duration_since к вашим услугам.

▪️Помните, я рассказывал про ненужные автовыводимые ограничения? Так вот, эти соображения применили к BuildHasherDefault, iter::Empty и future::Pending. Чуть меньше мусора в обобщённом коде, ура.

▪️Стабилизировали Vec::spare_capacity_mut, который возвращает неинициализированную часть ёмкости вектора в виде &mut [MaybeUninit<T>]. (А вот Vec::split_at_spare_mut — нет, так что наполнить вектор данными, исходя из уже инициализированных данных, нельзя)

▪️Вы когда-нибудь хотели сделать значение со счётчиком ссылок, которое держит слабую ссылку на себя? Не знаю, зачем вам это нужно, но теперь это можно сделать через конструктор Rc::new_cyclicArc::new_cyclic, разумеется)

На этом всё, как я уже говорил, полный список изменений в RELEASES.md.
👍2🔥2