Forwarded from shitposting 3.0 [+ dragons]
Telegram
яркие и красочные сны
я смотрю на это и у меня какая-то агрессия и зубы скрипят
Блог*
Photo
Кстати, одна из первых вещей, которая мне бросилась в глаза после прилёта — повсеместные национальные флаги. По крайней мере, в Ереване.
Непривычно.
Непривычно.
🔥8
Блог*
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 #моё
Что ж, наверное, стоит объяснить, а что это вообще за код такой.
Странно выглядят фрагменты вроде
Если переписать код, чтобы использовать лишь ASCII имена (и заодно привести его под принятые соглашения о наименованиях), то получится следующее:
Если вынести на верхний уровень impl-блоки (как я бы и сделал в реальном коде) и немного переупорядочить для ясности, то получится вот это:
ответ к загадке этого кода строчку, которая фактически является ассертом времени компиляции на то, что численное значение
Что ж, наверное, стоит объяснить, а что это вообще за код такой.
Странно выглядят фрагменты вроде
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). Но это обобщённая функция — так какой же тип там выводится?Telegram
Блог*
#prog #rust #rustreleasenotes
Да, да, я знаю, что я ленивая жягодица, не напоминайте.
Вышел Rust 1.53.0! Как обычно, я не буду разбирать релиз целиком, а выделю лишь то, что привлекло лично моё внимание.
Но начну я, пожалуй, с парочки вещей, которые мне…
Да, да, я знаю, что я ленивая жягодица, не напоминайте.
Вышел Rust 1.53.0! Как обычно, я не буду разбирать релиз целиком, а выделю лишь то, что привлекло лично моё внимание.
Но начну я, пожалуй, с парочки вещей, которые мне…
❤1😐1