Блог*
Открыть в Telegram
Блог со звёздочкой. Много репостов, немножко программирования. Небольшое прикольное комьюнити: @decltype_chat_ptr_t Автор: @insert_reference_here
Больше1 950
Подписчики
+2124 часа
+227 дней
+2230 день
Архив постов
1 947
#tips
Не используйте для подписи коммитов на Github GPG-ключи, у которых указан e-mail, но не имя — Github почему-то считает, что к этим ключам не прикреплены e-mail (
The email in this signature doesn’t match the committer email) и потому отказывается верифицировать коммиты, подписанные этим ключом.1 947
А для того, чтобы оно всё-таки работало, нужно, чтобы реализовали impl Fn() -> impl Trait in return position (привет, Вафель) и, желательно, синтаксис для явного написания замыкания с полиморфизмом высшего ранга.
И то, кстати, не факт, что сработает, поскольку в силу того, что возвращаемое замыкание содержит
&'o Order, это замыкание, вообще говоря, должно принимать ссылку на строковой слайс не с произвольным временем жизни, а живущую не менее, чем 'o, а ограничение на времена жизни в higher-ranked trait bounds не работают даже для fn pointers.1 947
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)1 947
...Или мы можем подрубить 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-ами. Как всегда, весь код в гисте.1 947
#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);
Не менее красиво мы можем отсортировать по имени и по байтам... А нет, погодите, не можем — это потребует возвращать пару ссылок, а кортеж из ссылок ссылкой не является 1 947
#prog #rust #article
И ещё немного про мьютексы в Rust.
Why Rust mutexes look like they do (перевод)
1 947
cat вступил в профсоюз и его теперь нельзя уволить с его работы на первом месте в пайплайне
1 947
Repost from Незаслуженный учитель России
Задал ребенку блестящее (не могу кривить душой!) задание.
Мы решили (так как беседуем с ней раз в неделю уже третий год), что пора обсуждать литературу. Решили начать с мифа. Ученица начиталась терминологии, объяснила мне, что такое космогонический миф, а что такое онтологический. Было очень здорово, хотя слова эти по первости она произносила по слогам. Мы приводили друг другу примеры, пересказывали мифы разных народов и оба собой глубоко гордились.
И тут ученица задала блестящий вопрос — а выросли ли мы уже, все человечество, из мифов? Я задумался и предположил, что нет. В качестве примера я привел детскую мифологию, объясняющую мир. Например, мне в деревенском детстве объясняли, что убивать пауков нельзя. Если убьешь паука, то Бог плачет и идет дождь. Девочка очень обрадовалась и стала толковать. Пауки полезны, они очищают дом от насекомых, их нельзя убивать; но надо объяснить, почему. Я к этому добавил, что рассказывали мне это про пауков-крестовиков, у которых на спинке буквально крест — отсюда и мотив Бога. Ученица в качестве своего детского мифа привела историю про аиста, приносящего детей.
И тут у меня в голове щелкнуло. Я поразмыслил и предложил ей провести исследование. Буквально расспросить одноклассников о мифах, в которые они верили в детстве и с помощью которых им объяснили мир. А еще лучше — расспросить родителей о том, во что они верили в детстве. Ученица прямо самостоятельно и без напоминания записала все в тетрадочку и сообщила, что это очень остроумное задание. Я не мог не согласиться и решил ей польстить.
— Вообще-то это можно даже назвать антропологическим исследованием! — важно сказал я.
Девочка округлила глаза и тут же записала такое важное и звучное выражение.
Буду держать вас в курсе.
1 947
Repost from partially unsupervised
Существует известная проблема из области социологии: как получить более или менее честные данные в количественном исследовании, если респонденты не хотят/боятся/стыдятся отвечать честно.
И вот случайно наткнулся на простой и изящный подход в духе differential privacy. TL;DR - формируем список из N утверждений, делим респондентов на две группы, одной показываем все N утверждений, другой - все, кроме того самого чувствительного. Вопрос формулируется как "с каким количеством утверждений (неважно, каких именно) вы согласны", и по разнице между группами легко вывести истинную поддержку.
Должно подойти не только для опросов про войну, но и для user research.
1 947
Repost from N/a
«У России три пути: вебкам, закладки и айти»
> OnlyFans приостанавливает работу аккаунтов из России
> полиция Германии ликвидирует Гидру
> Интел полностью останавливает работу в России
У России… нет путей?
