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

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

Небольшое прикольное комьюнити: @decltype_chat_ptr_t
Автор: @insert_reference_here
Download Telegram
How would you call a member of a sect?

An in-sect
🤔6👌2😁1
Што
🤣7
Правдивый #politota #meme
Forwarded from Huesitos y Peñíscola (Иван Бевуч)
😁18
😁20🖕4
Раз уж я начал про флаги. #meme
😁7🤮6🖕3🤬2❤‍🔥1😢1
Блог*
Photo
Кстати, одна из первых вещей, которая мне бросилась в глаза после прилёта — повсеместные национальные флаги. По крайней мере, в Ереване.

Непривычно.
🔥8
#meme про твиттер (и немного про #game)
Forwarded from ТГ Шевченка
Forwarded from shitposting 3.0 [+ dragons]
😁23
Блог*
I love Rust for letting me write self-describing code trait Т { type T; const TT: (Self::T, Self::T); } trait ТТ<T> { const Т: T; } trait Sеlf { const Sеlf: Self; } const fn t<T: Т<T = isize>>() -> isize { impl<T: ТТ<T>> Sеlf for T…
#prog #rust #моё

Что ж, наверное, стоит объяснить, а что это вообще за код такой.

Странно выглядят фрагменты вроде

trait Sеlf {
const Sеlf: Self;
}

и

const fn t<T: Т<T = isize>>

, поскольку они вроде бы не должны компилироваться из-за совпадающих имён. Дело в том, что я использовал в Sеlf кириллическую е (и в имени трейта, и в имени ассоциированного типа), а в именах трейтов Т и ТТ — соответственно кириллическую Т. Это разрешено с версии Rust 1.53. При этом компилятор выдаёт (помимо всего прочего) вот такое предупреждение на этот код:

warning: the usage of Script Group `Cyrillic` in this crate consists solely of mixed script confusables
--> src/lib.rs:1:7
|
1 | trait Т {
| ^
|
= note: the usage includes 'Т' (U+0422), 'е' (U+0435)
= note: please recheck to make sure their usages are indeed what you want
= note: `#[warn(mixed_script_confusables)]` on by default

И также указывает на пары идентификаторов, которые могут быть перепутаны. Полезный компилятор, как ни крути.

Если переписать код, чтобы использовать лишь ASCII имена (и заодно привести его под принятые соглашения о наименованиях), то получится следующее:

trait PairValue {
type T;
const PAIR_VALUE: (Self::T, Self::T);
}

trait GenericValue<T> {
const GENERIC_VALUE: T;
}

trait SelfValue {
const SELF_VALUE: Self;
}

const fn t<T: PairValue<T = isize>>() -> isize {
impl<T: GenericValue<T>> SelfValue for T {
const SELF_VALUE: Self = T::GENERIC_VALUE;
}
T::PAIR_VALUE.0 * T::PAIR_VALUE.1 + 20
}

const fn self_describing<T: SelfValue>() -> T {
impl<T: PairValue<T = isize>> GenericValue<isize> for T {
const GENERIC_VALUE: isize = t::<T>();
}
T::SELF_VALUE
}

enum The {
Answer = {
impl PairValue for isize {
type T = isize;
const PAIR_VALUE: (isize, isize) = (2, 11);
}
self_describing()
},
}
const _ASSERT: [(); 42] = [(); The::Answer as usize];

Вроде выглядит немного получше, но всё ещё запутанно. В Rust, как известно, (почти) всё является выражением. Различного рода top level определения (называемые item в reference) также могут быть использованы, как выражения, и при этом возвращают (). Очень много куда можно воткнуть impl-блок — в частности, внутри функции и внутри выражения. В том числе внутри выражения для дискриминанта варианта enum. Разумеется, при этом применяются обычные правила видимости: нельзя реализовать трейт, если трейт или тип, для которого они определяются, не видны в текущей области видимости, но сами impl-ы трейтов видны всюду, где виден сам трейт. Тут эти правила видимости, впрочем, не мешают.

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

trait PairValue {
type T;
const PAIR_VALUE: (Self::T, Self::T);
}

impl PairValue for isize {
type T = isize;
const PAIR_VALUE: (isize, isize) = (2, 11);
}

trait GenericValue<T> {
const GENERIC_VALUE: T;
}

const fn t<T: PairValue<T = isize>>() -> isize {
T::PAIR_VALUE.0 * T::PAIR_VALUE.1 + 20
}

impl<T: PairValue<T = isize>> GenericValue<isize> for T {
const GENERIC_VALUE: isize = t::<T>();
}

trait SelfValue {
const SELF_VALUE: Self;
}

impl<T: GenericValue<T>> SelfValue for T {
const SELF_VALUE: Self = T::GENERIC_VALUE;
}

const fn self_describing<T: SelfValue>() -> T {
T::SELF_VALUE
}

enum The {
Answer = self_describing(),
}

const _ASSERT: [(); 42] = [(); The::Answer as usize];

В последней строчке мы находим ответ к загадке этого кода строчку, которая фактически является ассертом времени компиляции на то, что численное значение The::Answer является 42. Откуда берётся это значение? Выше Answer присваивается значение вызова определённой парой строк выше функции self_describing (кстати, использовать трейты на обобщённых параметрах в const fn стало возможным с версии 1.61). Но это обобщённая функция — так какой же тип там выводится?
1😐1
Блог*
I love Rust for letting me write self-describing code trait Т { type T; const TT: (Self::T, Self::T); } trait ТТ<T> { const Т: T; } trait Sеlf { const Sеlf: Self; } const fn t<T: Т<T = isize>>() -> isize { impl<T: ТТ<T>> Sеlf for T…
Так вот, Rust reference гласит, что для default representation enum (а The является именно таковым, потому что на нём нет #[repr]-атрибутов) дискриминанты имеют тип isize (что, однако, не мешает компилятору выбрать тип поменьше для представления enum). Таким образом, обобщённый параметр у self_describing выводится равным возвращаемому типу, т. е. isize.

Так какое же значение возвращается? Пойдёт снизу вверх.

self_describing принимает тип, ограниченный трейтом SelfValue, и возвращает ассоциированную константу SELF_VALUE;
|SelfValue реализован для всех типов T, реализующих GenericValue<T>;
| |GenericValue<T> реализован для всех типов, реализующих PairValue<T = isize>;
| | |PairValue<T = isize> реализован для isize с PAIR_VALUE = (2, 11);
| |GenericValue<isize> реализован для isize с GENERIC_VALUE = t::<isize>();
| | |функция t принимает тип, ограниченный PairValue<T = isize>, и возвращает комбинацию от составляющих T::PAIR_VALUE;
| | |для isize функция t возвращает 2 * 11 + 20 = 42;
| |<isize as GenericValue<isize>>::GENERIC_VALUE = 42;
|<isize as SelfValue>::SELF_VALUE = 42;
self_describing::<isize>() == 42;

Иными словами,

enum The {
Answer = 42,
}

В последней строке создаётся неиспользуемая константа, тип которой задан, как массив длины 42, и которая инициализируется массивом длины The::Answer as usize, то есть 42. Так как типы слева и справа совпадают, то код компилируется (и выдаёт предупреждение о неиспользуемых The и self_describing).

То есть "Ответ на главный вопрос жизни, вселенной и всего такого" — это 42, что мы и проверили в Rust на этапе компиляции.
😐2
Парочка #meme про твиттер
#meme про... Ножницы
🌚17🍌4