Size: a a a

2020 January 15
Блог*
#meme
источник
Блог*
источник
Блог*
Когда-то я написал на Rust программу, которая переводила числа в строку прописью (т. е. 123 -> "сто двадцать три"). Написал и решил написать статью о том, как написать подобную программу.

Это было год назад.

Статья всё ещё не готова.
источник
2020 January 17
Блог*
#prog
Идея не нова, но убедительную реализацию сделать сложно
источник
Блог*
художник @harryh___h экспериментирует с рендером, который выглядит так, будто листья нарисованы от руки. сделано в Unity
источник
2020 January 18
Блог*
источник
Блог*
#prog #article
Отличная статья, показывающая новый способ тестирования, вскрывающий ошибки...
А заодно хорошо показывающая, насколько мы далеки от написания по-настоящему корректно работающего софта
источник
Блог*
Вот эта статья вынесла мозги (друзьям и мне). Да, все же ловить сегфолты в Постгресе неприкольно, хотя хорошо, что скоро будет пофикшено.

The core idea of this approach is to automatically generate queries for which we ensure that they fetch a specific, randomly selected row, called the pivot row. If the DBMS fails to fetch the pivot row, the likely cause is a bug in the DBMS. We tested our approach on three widely-used and mature DBMS, namely SQLite, MySQL, and PostgreSQL. In total, we reported 123 bugs in these DBMS, 99 of which have been fixed or verified, demonstrating that the approach is highly effective and general.

https://arxiv.org/abs/2001.04174
источник
2020 January 19
Блог*
dereference_pointer_there
#prog #rust #моё
Во время решения одной из задач Advent of Code мне пришлось решать довольно заковыристую в плане владения задачу: требовалось связать цепочку интерпретаторов так, чтобы данные, выдаваемые одним экземпляром интерпретатора, использовались в качестве входных данных следующего интерпретатора.

Дополнительной сложности придал тот факт, что цепочка замкнута в петлю, поэтому просто пройтись по цепочке один раз было недостаточно. Здесь напрашиваются сопрограммы, но их в Rust нет, поэтому мне пришлось сделать дек очередей в качестве буферов для данных. В итоге в моём решении были строки вида:

buffers.push_back(input_buffer);
input_buffer = output_buffer;
output_buffer = buffers.pop_front().unwrap();

Мало того, что это выглядит уродливо и многословно, так ещё и unwrap здесь совершенно лишний: после того, как в дек положили элемент, в нём обязательно есть как минимум один элемент, поэтому pop_front заведомо вернёт Some(_).

Что же мне нужно было на самом деле? Кольцевой буфер очередей, которой обеспечивают мутабельный доступ к парам идущих подряд элементов. std::collections::VecDeque обеспечивает доступ по индексам, но  моей проблемы это бы не решает, так как таким образом можно получить только доступ к отдельным элементам, но не к, скажем, внутреннему слайсу, а взять две мутабельные ссылки одновременно даже по двум заведомо разным индексам мне не дал бы borrow checker.

В тот раз я пошёл по пути наименьшего распределения и оставил тот код,  который я показал выше, но потом у меня возникла мысль, что нужного мне поведения можно было бы достичь через обёртку, которая предоставляла бы нужный мне доступ через unsafe-преобразования. Такую я в итоге и написал: https://gist.github.com/AnthonyMikh/bb41179b20938ba329c3d411ddc1c679.  Вот как она выглядит:

struct MutPair<T> {
   inner: VecDeque<T>,
   first: usize,
}

Здесь хранится только один из индексов пары, потому что второй всегда можно подсчитать, зная его и длину inner. Основной метод этого типа - get, который возвращает две мутабельные ссылки, на элемент с текущим индексом и следующим за ним:

pub fn get(&mut self) -> (&mut T, &mut T) {
   let first = self.first;
   let second = self.incremented();
   let (head, tail, head_len) = {
       let (head, tail) = self.inner.as_mut_slices();
       let len = head.len();
       (head.as_mut_ptr(), tail.as_mut_ptr(), len)
   };

   let first = unsafe { &mut *get_mut(head, tail, head_len, first) };
   let second = unsafe { &mut *get_mut(head, tail, head_len, second) };

   (first, second)
}

Здесь as_mut_slices возвращает мутабельные ссылки на содержимое дека (слайсов два, потому что логически единая последовательность элементов может начинаться перед концом внутреннего вектора и продолжаться от его конца). Borrow checker не даёт нам в лоб взять две мутабельные ссылки, поэтому мы используем стандартный трюк: кастуем ссылки в сырые указатели, которые компилятором уже не отслеживаются, сдвигаем их и преобразуем обратно в ссылки. Вспомогательная функция get_mut возвращает нужный нам элемента по индексу, учитывая, что последовательность по факту состоит из двух слайсов.

Считаю ли я итоговый код завершённым? Почти: я бы хотел хранить impl BorrowMut<VecDeque<T>>, чтобы MutPair можно было использовать тогда, когда невозможно отдать владение деком.
#prog #rust #моё
В конце этой заметки я сказал, что планирую расширить код до поддержки impl BorrowMut<VecDeque<T>>. Теперь я понимаю, что это несколько неосмотрительно: код в unsafe полагается на то, что адрес, по которому лежат данные, не меняется. В случае impl BorrowMut такой гарантии уже нет. Для доказательства того, что подобный вариант является unsound, предположим, что MutPair уже реализовал подобный функционал (это изменение я, с вашего позволения, опущу, потому что это чисто механическое изменение кода) и рассмотрим следующий тип:

#[derive(Default)]
struct DeqWrapper<T> {
   inner: VecDeque<T>,
}

Для него можно написать следующие реализации Borrow и BorrowMut:

impl<T> Borrow<VecDeque<T>> for DeqWrapper<T> {
   fn borrow(&self) -> &VecDeque<T> {
       &self.inner
   }
}

impl<T> BorrowMut<VecDeque<T>> for DeqWrapper<T> {
   fn borrow_mut(&mut self) -> &mut VecDeque<T> {
       self.inner.clear();
       &mut self.inner
   }
}

Да, это отвратительно, да, так никто (я надеюсь) не пишет. Но принципиально нас ничего от этого не ограждает. Хочу отметить, что код выше не содержит unsafe.

Теперь попытаемся использовать данный тип:

let wrapper = DeqWrapper::<i32>::default();
wrapper.inner.push_back(0);
wrapper.inner.push_back(1);
let mut mut_pair = MutPair::new(wrapper).unwrap(); // (1)
let _ = mut_pair.get(); // (2)

Что тут происходит? В (1) мы делаем внутри MutPair::new проверку длины дека. Так как при этом мы используем .borrow(), DeqWrapper даёт ссылку на поле inner, длина которого как раз достаточна, поэтому MutPair возвращает Ok и .unwrap() не паникует. В (2) мы вызываем .borrow_mut(), из дека удаляется данные, MutPair::get использует get_mut, предполагая, что данные есть, и — вуаля! — мы получаем ссылку на неинициализированные данные! Что является UB в Rust.

Хочу особо отметить, что мы получили UB, не написав (как пользователь) unsafe-код и не вызвав unsafe-код напрямую, поэтому наш код unsound.
источник
Блог*
dereference_pointer_there
#prog #rust #моё
В конце этой заметки я сказал, что планирую расширить код до поддержки impl BorrowMut<VecDeque<T>>. Теперь я понимаю, что это несколько неосмотрительно: код в unsafe полагается на то, что адрес, по которому лежат данные, не меняется. В случае impl BorrowMut такой гарантии уже нет. Для доказательства того, что подобный вариант является unsound, предположим, что MutPair уже реализовал подобный функционал (это изменение я, с вашего позволения, опущу, потому что это чисто механическое изменение кода) и рассмотрим следующий тип:

#[derive(Default)]
struct DeqWrapper<T> {
   inner: VecDeque<T>,
}

Для него можно написать следующие реализации Borrow и BorrowMut:

impl<T> Borrow<VecDeque<T>> for DeqWrapper<T> {
   fn borrow(&self) -> &VecDeque<T> {
       &self.inner
   }
}

impl<T> BorrowMut<VecDeque<T>> for DeqWrapper<T> {
   fn borrow_mut(&mut self) -> &mut VecDeque<T> {
       self.inner.clear();
       &mut self.inner
   }
}

Да, это отвратительно, да, так никто (я надеюсь) не пишет. Но принципиально нас ничего от этого не ограждает. Хочу отметить, что код выше не содержит unsafe.

Теперь попытаемся использовать данный тип:

let wrapper = DeqWrapper::<i32>::default();
wrapper.inner.push_back(0);
wrapper.inner.push_back(1);
let mut mut_pair = MutPair::new(wrapper).unwrap(); // (1)
let _ = mut_pair.get(); // (2)

Что тут происходит? В (1) мы делаем внутри MutPair::new проверку длины дека. Так как при этом мы используем .borrow(), DeqWrapper даёт ссылку на поле inner, длина которого как раз достаточна, поэтому MutPair возвращает Ok и .unwrap() не паникует. В (2) мы вызываем .borrow_mut(), из дека удаляется данные, MutPair::get использует get_mut, предполагая, что данные есть, и — вуаля! — мы получаем ссылку на неинициализированные данные! Что является UB в Rust.

Хочу особо отметить, что мы получили UB, не написав (как пользователь) unsafe-код и не вызвав unsafe-код напрямую, поэтому наш код unsound.
В чём же проблема? Наш код слепо доверял реализации BorrowMut, чего делать не стоит (unsafe не должен доверять safe).

Каковы решения? Лично я вижу такие:
1) Объявить методы MutPair как unsafe. В принципе, вариант, но это немного лишает смысла эту абстракцию.
2) Оставить код как есть. Вполне рабочий вариант, но не исключено, что для кого-то это окажется недостаточно гибким.
3) Обобщить код относительно sealed-трейта и включить в него только доверенные реализации (для VecDeque<T> и &mut VecDeque<T>).  Тоже рабочий вариант (если я таки буду публиковать этот код на crates.io, то, вероятно, так и поступил бы), но это выглядит несколько избыточным решением + страдает эргономика: пользователю теперь надо импортировать нужный трейт, который он(а) не может реализовать.
99) Требовать от типов реализовать трейт, который не только даёт мутабельную ссылку на VecDeque, но и требует доказательство вменяемости такой реализации. К сожалению, этот вариант сильно превосходит текущие возможности Rust и навряд ли когда-то будет осуществлён.
источник
Блог*
#meme
источник
Блог*
источник
Блог*
#meme
источник
Блог*
источник
Блог*
#prog #video
Как писать тесты (если быть более точным — тесты для property-based тестирования).
Лично для меня поразила идея metamorphic testing — простая и вместе с тем плодотворная.
https://youtu.be/zvRAyq5wj38
источник
2020 January 21
Блог*
источник
Блог*
В копилку когнитивных искажений: ru.wikipedia.org/wiki/Парадокс_Абилина
Кажется, нужен новый тег... #psy?
источник
Блог*
#prog #article

As closing thoughts: this blog post is not intended to start a flame war, nor is it intended to be an assault on dynamically typed programming. There are many patterns in dynamically-typed languages that are genuinely difficult to translate into a statically-typed context, and I think discussions of those patterns can be productive. The purpose of this blog post is to clarify why one particular discussion is not productive, so please: stop making these arguments. There are much more productive conversations to have about typing than this.

https://lexi-lambda.github.io/blog/2020/01/19/no-dynamic-type-systems-are-not-inherently-more-open/
источник
Блог*
#quotes
источник
Блог*
Ну и правильно писать на го нифига не просто, весь этот маркетинговый булщит работает только под хорошим менторством и ревью от синьеров
источник