1.83K subscribers
3.3K photos
131 videos
15 files
3.57K links
Блог со звёздочкой.

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

Небольшое прикольное комьюнити: @decltype_chat_ptr_t
Автор: @insert_reference_here
Download Telegram
👨📅🔄 ➡️ 3️⃣🍁
#prog #rust #embedded #rustlib #amazingopensource

Исторически одной из проблем на пути использования Rust в встраиваемых устройствах был тот факт, что стандартная машинерия для форматирования сильно раздувает размер результирующего бинарника. Это особенно неприятно в силу того, что стандартная паника использует эту машинерию. Библиотека defmt от Ferrous System нацелена на решение это проблемы: она позволяет использовать форматирование (в том числе отложенное) и паники, сохраняя при этом маленький размер бинарника.
#prog #article

"So, although I don’t have any data to back this up, I strongly suspect that experienced programmers rarely spend time posting about how they program because they just don’t think it’s anything special.
But they should! It may not be special, but it’s necessary, and if good programmers don’t start posting about how to do good programming, we’ll never get out of this nasty place where everyone has to go through six years of writing horrible object-oriented programs before they realize they’re wasting their time."

caseymuratori.com/blog_0015
Forwarded from The After Times
Forwarded from The After Times
#prog #rust #article

Буквально мои мысли читает. TL;DR: если у вас есть Foo и FooBuilder, добавьте метод Foo::builder.

matklad.github.io/2020/08/12/who-builds-the-builder.html
#prog #rust #rustlib #article

Разбор библиотеки для автоматизации конверсии Cell<Struct> -> Cell<Field>.

abubalay.com/blog/2020/01/05/cell-field-projection
Вызывающий прения #prog #meme
#prog #rust #article

Я считаю, что в дизайне современных языков программирования упущена весьма важная тема: перекрывающийся доступ к значениям (aliasing). В статье Niko Matsakis (старой, написанной ещё в те времена, когда в Rust ещё был сборщик мусора) раскрывается связь между управлением памятью и отсутствием гонок данных. В статье Manish Goregaokar эта тема раскрыто несколько более глубоко, а в статье Matt Brubeck рассказывается, в частности, о том, как связаны мутабельность и уникальность доступа и как можно построить безопасные API поверх небезопасного, предоставляющего безусловное перекрытие доступа.

И да, если вы считаете, что это исключительно бзик Rust, то вот вам статьи о аналогичных механизмах в Swift и D.
#prog #rust #моё

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

Я задумался над тем, как можно упростить подобное решение, не потеряв эффективности мутабельных изменений, и, кажется, нашёл тот краеугольный камень, с помощью которого можно выстроить такую инфраструктуру, подобно тому, как все методы Iterator определены через Iterator::next. Это трейт Apply:

trait Apply {
type Patch;
fn apply(&mut self, patch: Self::Patch) -> Self::Patch;
}


Дополнительное требование к реализации, которое, увы, невыразимо в коде на Rust (но было бы выразимо на, скажем, Agda или Idris), состоит в том, что изменение, возвращённое apply, будучи применённым обратно к self, переводит его в первоначальное состояние (до степени, требуемой задачей: это изменение может поменять, скажем, ёмкость хэшмапы, но навряд ли для вас это существенно). Иными словами,

let changed = {
let mut changed = original.clone();
let undo = changed.apply(change);
changed.apply(undo);
changed
};
assert_eq!(original, changed);


Данное определение вкупе с приведённым выше требованием обеспечивает композируемость и, как следствие, линейную историю. Скажем,

let undo1 = state.apply(change1);
let undo2 = state.apply(change2);
let undo3 = state.apply(change3);
state.apply(undo3);
state.apply(undo2);
state.apply(undo1); // сейчас state находится в том же
// состоянии, что и до начала преобразований

Разумеется, хранить историю можно и в более организованном виде. Скажем, вот как организовывается обобщённая структура для поддержки отмены изменений:

struct Undo<Data: Apply> {
data: Data,
changes: Vec<Data::Patch>,
}

impl<Data: Apply> Undo {
fn get(&self) -> &Data {
&self.data
}
fn apply(&mut self, patch: Data::Patch) {
self.changes.push(self.data.apply(patch));
}
fn undo(&mut self) {
if let Some(undo) = self.changes.pop() {
self.data.apply(undo);
}
}
}


Если несколько изменить способ хранений изменений и не отбрасывать их, то можно также реализовать redo.

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

struct Patching<Data>(Data);
struct Unapply<'a, Data: Apply> {
data: &'a mut Data,
undo: Option<Data::Patch>,
}

impl<Data: Apply> Patching<Data> {
fn applying(&mut self, patch: Data::Patch) -> Unapply<Data> {
let undo = self.0.apply(patch);
Unapply {
data: &mut self.0,
undo: Some(undo),
}
}
}

impl<Data: Apply> Unapply<'_, Data> {
fn applying<'a>(&'a mut self, patch: Data::Patch) -> Unapply<'a, Data> {
let undo = self.data.apply(patch);
Unapply {
data: &mut self.data,
undo: Some(undo),
}
}
}

impl<Data: Apply> Drop for Unapply<'_, Data> {
fn drop(&mut self) {
let undo = self.undo.take().unwrap();
self.data.apply(undo);
}
}


Этот метод уже по эргономике приближается к варианту с персистентными структурами данных: если мы в каждый момент времени работаем только с одной версией структуры, то этого достаточно, при этом нам не требуется применять отмену руками, всё происходит автоматически.
👍2
По идее, здесь должен был быть мотивирующий пример, показывающий, как вся эта машинерия упрощает код, и я думал о том, чтобы сделать решатель судоку... А потом понял, что набор всех возможных значений для всех 9 ⨯ 9 = 81 клеток поля занимает настолько мало места, что его проще копировать целиком, чем содержать целый список изменений (либо в куче, либо неявно на стеке, как в случае с Unapply), так что у меня пропала мотивация делать этот относительно сложный пример. Может, вы сможете что-то подсказать?
#prog #rust #parsing #article

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

В этой статье рассказывается о консервативном расширении PEG, добавляющем семантику восстановления после ошибки разбора. Чуда не случилось в том смысле, что много чего надо писать руками, но авторам удалось побить ANTLR — как по качеству сообщений об ошибках, так и по быстроте работы.
А вот также статья, адаптирующая этот подход к парсер-комбинаторам (конкретно к nom)
SFINAE надо было назвать SNAFU
#prog #rust #article

Как заставить программы на Rust работать с длинными путями на Windows.

gal.hagever.com/posts/windows-long-paths-in-rust/