Size: a a a

2020 June 14
Блог*
#prog

Tckb ds rjulf-yb,elm gbcfkb d nthvbyfkt git dvtcnj пше, nj, djpvj;yj, dfv ghbujlbncz 'nf ghjuhfvvf, rjnjhfz fdnjvfnbxtcrb bcghfdkztn zpsr yf ghfdbkmysq.

github.com/danakt/pshe
источник
Блог*
dereference_pointer_there
#prog

Tckb ds rjulf-yb,elm gbcfkb d nthvbyfkt git dvtcnj пше, nj, djpvj;yj, dfv ghbujlbncz 'nf ghjuhfvvf, rjnjhfz fdnjvfnbxtcrb bcghfdkztn zpsr yf ghfdbkmysq.

github.com/danakt/pshe
Аналогичной полезности вещь: перевод текста из одной раскладки в другую.

raskladki.net.ru
источник
Блог*
dereference_pointer_there
Аналогичной полезности вещь: перевод текста из одной раскладки в другую.

raskladki.net.ru
А если вам нужно понять, в какой же, блин, кодировке этот текст в кракозябрах, то есть расшифровщик от студии Лебедева (да, того самого).

artlebedev.ru/decoder
источник
2020 June 16
Блог*
#science

Спасибо, Илья
источник
Блог*
источник
2020 June 17
Блог*
#prog #rust #amazingopensource

Хозяйке на заметку

Подборка библиотек для работы с serde от замечательного Толяна dtolnay.

erased-serde — трейты из serde со стёртыми типами. Позволяют сделать из (де)сериализаторов трейт-объекты. Обычно это запрещено правилами object safety из-за наличия обобщённых типов.

typetag — сериализация и десериализация трейт-объектов. Работает на костылях.

serde-repr — делегирует (де)сериализацию C-like enum его числовому значению.

serde-stacker — позволяет десериализовывать глубоко вложенные структуры, динамически выделяя память под стек. Работает, увы, не на всех OS: нормально работает на linux и MacOS и течёт на Windows.

serde-ignored — позволяет обернуть десериализатор с кастомный коллбеком, который будет вызываться на всех проигнорированных в процессе десериализации ключах.
источник
Блог*
"Призрачный гонщик".

Участковый во время сухого закона пытается обнаружить в Череповце производителя сивухи.
источник
Блог*
dereference_pointer_there
Так, а что тут не так? И почему не совпадают типы?

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

Зачем так сложно? В этом же нет никакого смысла!

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

fn main() {
   assert_eq!(std::mem::size_of_val(&add_one), 0);
   let add_one_ptr: fn(u32) -> u32 = add_one;
   assert_eq!(
       std::mem::size_of_val(&add_one_ptr),
       std::mem::size_of::<usize>()
   );
}

Кстати, на замыкания это тоже распространяется: значение замыкания хранит в себе только захваченные данные, поэтому размер замыкания равен (с поправкой на выравнивание) размеру захваченных данных. Если замыкание ничего не захватывает, то у него нулевой размер:

fn main() {
   assert_eq!(std::mem::size_of_val(&|x: u32| x + 1), 0);
}

Теперь я вижу, что в этом действительно есть смысл. И всё-таки, как скомпилировать тот пример с парой функций?

Просто хранить разнородную пару.

А если нам принципиально, чтобы элементы пары имели одинаковый тип?

В таком случае нам надо привести значения к одинаковому типу. Это можно сделать так:

let funcs: Pair<fn(_) -> _> = (add_one, add_two);

Обратите внимание, выписывать тип точно в этом случае не нужно, достаточно сказать, что это function pointer. Другой способ — явно привести одно из значений в паре:

let funcs: Pair<_> = (add_one as fn(_) -> _, add_two);

Второе значение будет неявно приведено (coerced) к нужному типу. В принципе, ничто не мешает написать явный каст у обоих значений, но надобности в этом в данном примере нет. Кстати, если мы сделаем массив функций, то никаких кастов делать не придётся, rustc сам уравняет типы до функционального указателя:

let arr = [add_one, add_two];

А что, если замыкание возвращает захваченное значение? Если это значение не копируемо, то замыкание можно вызвать только один раз...

А вот про это я расскажу в следующий раз.
#prog #rust #моё

Хроники замыканий

Как я уже говорил, для каждого литерала замыкания компилятор Rust создаёт свой собственный анонимный тип, в котором содержатся захваченные замыканием переменные. Эти сгенерированные типы объединяет одно: они реализуют один или несколько Fn-трейтов: core::ops::{FnOnce, FnMut, Fn}.

А почему их три? Разве нельзя обойтись одним?

Можно, но это серьёзно ограничило бы возможности замыканий. Трейтов три из-за того, что замыкания могут иметь различные возможности. Как ты помнишь, методы в Rust принимают первым параметром self, &mut self или &self (пошёл нафиг, arbitrary self types!). Соответственно и определения у Fn*-трейтов отличаются. Вот как они выглядят:

trait FnOnce<Args> {
   type Output;
   extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
}

trait FnMut<Args>: FnOnce<Args> {
   extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output;
}

pub trait Fn<Args>: FnMut<Args> {
   extern "rust-call" fn call(&self, args: Args) -> Self::Output;
}

Ти́повый параметр Args в сгенерированных реализациях — это кортеж, элементы которого — типы аргументов, по одному на каждый аргумент. Ассоциированный тип Output — это тип значения, возвращаемого при вызове замыкания. Методы трейтов объявлены с соглашением о вызове rust-call. Это, в частности, означает, что их нельзя сохранить или передать там, где ожидаются обычные функции.

Метод call трейта FnOnce принимает замыкание по значению, т. е. при вызове замыкание перемещается и его после этого нельзя использовать. Этот трейт не имеет никаких ограничений, что логично: если у нас есть замыкание, то его всегда можно вызвать как минимум один раз. В принципе, замыкание может не реализовывать другие трейты:

struct A;

let a = A;
let closure = || a;
closure();
// closure(); // use of moved value: closure

Здесь мы объявляем тип значение a типа A, которое не является копируемым, и замыкание, которое это значение захватывает и возвращает при вызове. Замыкания в Rust, если не указано ключевое слово move, захватывают значение по ссылке (возможно, мутабельной). Здесь a возвращается, поэтому его можно захватить только по значению. Так как это значение не копируемо, после вызова это значение перемещается из структуры данных, реализующей замыкание, и более замыкание вызвать нельзя. Собственно, это нам и скажет компилятор, если мы расскомментируем последную строку.

Окей, а что с другими трейтами?

Они дают возможность для более интересных замыканий. FnMut имеет в качестве супертрейта FnOnce — как я уже сказал, любое замыкание можно вызвать как минимум раз. Метод этого трейта call_mut принимает &mut self, а потому замыкание, во-первых, можно вызвать несколько раз, а во-вторых, может менять захваченные значения. Вот пример:

let mut n = 0;
let mut counter = || {
   n += 1;
   n
};
println!("{}", counter()); // 1
println!("{}", counter()); // 2
println!("{}", counter()); // 3

Здесь мы сделали переменную счётчик n и замыкание, которое его инкрементирует при каждом вызовом перед тем, как вернуть. Что тут важно — в силу того, что call_mut требует &mut self, а мутабельную ссылку можно взять только от мутабельного значения, нам пришлось объявить само замыкание как мутабельное. Если этот mut опустить, то компилятор резонно откажется компилировать код.

Понятно, а call принимает значение по ссылке и потому замыкание можно вызывать несколько раз, но оно не может менять захваченные значения?

Именно так. Более того, Fn является субтрейтом FnMut, и это тоже логично: если мы можем вызвать метод по разделяемой ссылке, то можем вызвать и по уникальной. Надо только иметь в виду, что &mut в Rust вообще-то означает не изменяемость, а уникальность, поэтому из-за interior mutability пример со счётчиком можно переделать и с "иммутабельным" замыканием.
источник
Блог*
dereference_pointer_there
#prog #rust #моё

Хроники замыканий

Как я уже говорил, для каждого литерала замыкания компилятор Rust создаёт свой собственный анонимный тип, в котором содержатся захваченные замыканием переменные. Эти сгенерированные типы объединяет одно: они реализуют один или несколько Fn-трейтов: core::ops::{FnOnce, FnMut, Fn}.

А почему их три? Разве нельзя обойтись одним?

Можно, но это серьёзно ограничило бы возможности замыканий. Трейтов три из-за того, что замыкания могут иметь различные возможности. Как ты помнишь, методы в Rust принимают первым параметром self, &mut self или &self (пошёл нафиг, arbitrary self types!). Соответственно и определения у Fn*-трейтов отличаются. Вот как они выглядят:

trait FnOnce<Args> {
   type Output;
   extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
}

trait FnMut<Args>: FnOnce<Args> {
   extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output;
}

pub trait Fn<Args>: FnMut<Args> {
   extern "rust-call" fn call(&self, args: Args) -> Self::Output;
}

Ти́повый параметр Args в сгенерированных реализациях — это кортеж, элементы которого — типы аргументов, по одному на каждый аргумент. Ассоциированный тип Output — это тип значения, возвращаемого при вызове замыкания. Методы трейтов объявлены с соглашением о вызове rust-call. Это, в частности, означает, что их нельзя сохранить или передать там, где ожидаются обычные функции.

Метод call трейта FnOnce принимает замыкание по значению, т. е. при вызове замыкание перемещается и его после этого нельзя использовать. Этот трейт не имеет никаких ограничений, что логично: если у нас есть замыкание, то его всегда можно вызвать как минимум один раз. В принципе, замыкание может не реализовывать другие трейты:

struct A;

let a = A;
let closure = || a;
closure();
// closure(); // use of moved value: closure

Здесь мы объявляем тип значение a типа A, которое не является копируемым, и замыкание, которое это значение захватывает и возвращает при вызове. Замыкания в Rust, если не указано ключевое слово move, захватывают значение по ссылке (возможно, мутабельной). Здесь a возвращается, поэтому его можно захватить только по значению. Так как это значение не копируемо, после вызова это значение перемещается из структуры данных, реализующей замыкание, и более замыкание вызвать нельзя. Собственно, это нам и скажет компилятор, если мы расскомментируем последную строку.

Окей, а что с другими трейтами?

Они дают возможность для более интересных замыканий. FnMut имеет в качестве супертрейта FnOnce — как я уже сказал, любое замыкание можно вызвать как минимум раз. Метод этого трейта call_mut принимает &mut self, а потому замыкание, во-первых, можно вызвать несколько раз, а во-вторых, может менять захваченные значения. Вот пример:

let mut n = 0;
let mut counter = || {
   n += 1;
   n
};
println!("{}", counter()); // 1
println!("{}", counter()); // 2
println!("{}", counter()); // 3

Здесь мы сделали переменную счётчик n и замыкание, которое его инкрементирует при каждом вызовом перед тем, как вернуть. Что тут важно — в силу того, что call_mut требует &mut self, а мутабельную ссылку можно взять только от мутабельного значения, нам пришлось объявить само замыкание как мутабельное. Если этот mut опустить, то компилятор резонно откажется компилировать код.

Понятно, а call принимает значение по ссылке и потому замыкание можно вызывать несколько раз, но оно не может менять захваченные значения?

Именно так. Более того, Fn является субтрейтом FnMut, и это тоже логично: если мы можем вызвать метод по разделяемой ссылке, то можем вызвать и по уникальной. Надо только иметь в виду, что &mut в Rust вообще-то означает не изменяемость, а уникальность, поэтому из-за interior mutability пример со счётчиком можно переделать и с "иммутабельным" замыканием.
Ох, постараюсь уложить это в голове. Есть ещё что-то, что нужно знать о замыканиях?

Да. Иногда требуется одно и тоже замыкание передать в качестве аргумента в несколько функций. Клонировать в этом случае не получится, потому что замыкание является клонируемым не всегда (а именно — начиная с версии 1.26.0, тогда, когда все захваченные значения клонируемы). Именно поэтому в стандартной библиотеке есть несколько blanket impl-ов, которые реализуют Fn*-трейты для ссылок на замыкания (например, вот). Поэтому, если, скажем, в двух функциях требуется замыкание, реализующее Fn(i32) -> i32, то можно сделать замыкание и передавать в качестве аргумента ссылку на него. В некоторых случаях сама функция требует ссылку на замыкание (хотя это, вообще говоря, странно). В таком случае можно взять ссылку непосредственно от литерала замыкания. Выглядит это несколько странно, но работает.

Видимо, на этом всё?

Отнюдь, о замыканиях можно рассказать ещё кое-что... Но это уже, видимо, тема для следующего поста. И будем надеяться, что его не придётся ждать ещё пару месяцев.
источник
Блог*
dereference_pointer_there
Думаю, теперь вы лучше понимаете, что тут происходит во втором сниппете
источник
2020 June 19
Блог*
#video

Я просто оставлю это здесь

youtube.com/watch?v=crdYIUdUOhc
youtube.com/watch?v=4JtOHr4IJHQ
источник
Блог*
#prog #quotes
источник
Блог*
Помню, мы как-то собирались у кого-то дома с ноутбуками и поднимали упоротую сеть такого вида:
(интернет) -eth- (PC) -eth- (ноутбук) -wifi- (ноутбук) -eth- (ноутбук)
источник
Блог*
Переслано от codingteam@cjr
Orhideous
Топология SOSIG
источник
Блог*
#prog #rust

Компилятор Rust не очень быстрый, но программисты часто усугубляют ситуацию жирными зависимостями. В частности (что лично мне тоже жутко не нравится), используют регулярное выражение там, где достаточно простого самописного парсера.

https://blog.kodewerx.org/2020/06/the-rust-compiler-isnt-slow-we-are.html
источник
Блог*
#prog #quotes
источник
Блог*
Переслано от (((Mike Lubinets)))
источник
Блог*
dereference_pointer_there
Переслано от (((Mike Lubinets)))
Если кому-то сложно читать, то вот то же самое текстом: https://t.me/ebanatics_learn/22746
Telegram
Nick Linker in Задачник по ебанатике или "Что такое ФП"
Чуть не вытекли глаза...
Здравствуй, <дорогой любитель ФП>

Спасибо за интересный вопрос Я выходные посмотрю эту библиотеку и напишу своё впечатление о ней. А пока поделюсь общими соображениями о функциональном и декларативном подходе.

В некоторых ситуациях, например, при разработке очень больших и сложных информационных систем на основе DDD применение функционального и декларативного подхода в каких-то рутинных частях СИстемы бывает полезно и позволяет писать в несколько раз меньше рутинного кода. Однако надо понимать, что функциональное и декларативное программирование, это не развитие, а наоборот вырождение упрощение программирование в сторону "программирования мышкой. Эти подходы применяются не для поднятия качества информационной системы на новый уровень, а для ускорения разработки и уменьшения требований к разработчикам, за счет снижения качества ПО и увеличения требования к железу. Для того чтобы делать какую-то рутинную работу при производстве ПО больших информационных систем могли натуральные обезьяны…
источник
2020 June 20
Блог*
#prog #meme (thanks @Psilon)
источник
Блог*
Донаты! Донаты?
Анонимный опрос
10%
Назови счёт, и я буду кидаться деньгами в монитор!
54%
Слышь, продолжай посты делать за спасибо
36%
😐
Проголосовало: 139
источник