Блог*
رفتن به کانال در Telegram
Блог со звёздочкой. Много репостов, немножко программирования. Небольшое прикольное комьюнити: @decltype_chat_ptr_t Автор: @insert_reference_here
نمایش بیشتر1 920
مشترکین
اطلاعاتی وجود ندارد24 ساعت
-17 روز
-1130 روز
آرشیو پست ها
1 920
#prog #video
Доклад от разработчиков IDE про превратности парсинга #cpp
youtube.com/watch?v=WfIr7lKT4Sk
1 920
Этот пост спонсирован редактором ace (используемом на Rust playground).
ace — редактор со сломанным поиском.
1 920
Так вот, 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 на этапе компиляции.1 920
#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 920
Кстати, одна из первых вещей, которая мне бросилась в глаза после прилёта — повсеместные национальные флаги. По крайней мере, в Ереване.
Непривычно.
اکنون در دسترس! پژوهش تلگرام ۲۰۲۵ — مهمترین بینشهای سال 
