Rust: Проблема отслеживания для константных дженериков (RFC 2000)

Созданный на 15 сент. 2017  ·  202Комментарии  ·  Источник: rust-lang/rust

Проблема с отслеживанием для rust-lang / rfcs # 2000

Обновления:

Если вы хотите помочь, ознакомьтесь с проблемами открытых константных дженериков и не стесняйтесь пинговать @varkor , @eddyb , @yodaldevoid , @ oli-obk или @lcnr, чтобы получить помощь в начале работы!


Блокировка стабилизации:

  • [ ] Дизайн:

    • [] Разрешение упорядочивания параметров типа const и type с параметрами по умолчанию

    • [] Решите, каков наилучший баланс затрат на UX / реализацию для унификации абстрактных константных выражений.

    • [] Как мы определяем правильность константных выражений.

  • [x] Реализация
  • [ ] Документация

    • [] руководство по rustc


Остающиеся проблемы с реализацией:

  • [] Разрешить различные комментарии FIXME(const_generics) .
  • [] Устранение проблем с канонизацией / отложенной нормализацией.
  • [] Изучить обработку константных параметров в шаблонах.
  • [] Добавить еще тесты.
  • [] Реализовать значения по умолчанию для константных параметров ( FIXME(const_generics:defaults) ).
  • [] Исправьте другие проблемы с A-const-generics .
  • [] Аудит использования has_infer_types .
  • [x] Запретите сложные выражения для константных аргументов, включающих параметры (на данный момент), например, {X * 2} .
  • [] Аудит диагностики (например, https://github.com/rust-lang/rust/pull/76401#discussion_r484819320).
A-const-fn A-const-generics A-typesystem B-RFC-approved C-tracking-issue F-const_generics T-compiler T-lang requires-nightly

Самый полезный комментарий

Вот краткий обзор достижений константных дженериков.


Прежде чем можно было бы должным образом начать работу над константными дженериками, необходимо было провести некоторый рефакторинг. @jplatte взяла на себя задачу https://github.com/rust-lang/rust/pull/45930. Затем @jplatte начал работать над основной реализацией константных дженериков, но обнаружил, что у них недостаточно времени для продолжения.

@yodaldevoid и я продолжили с того места, где остановился @jplatte , но быстро обнаружили, что прогресс https://github.com/rust-lang/rust/pull/48149 , https://github.com/rust-lang/rust/pull / 48452 , https://github.com/rust-lang/rust/pull/48523 , https://github.com/rust-lang/rust/pull/51880.

После этого можно было бы всерьез приступить к реализации константных дженериков. С тех пор мы с @yodaldevoid медленно, но верно добавляли поддержку дженериков const: https://github.com/rust-lang/rust/pull/58191 , https://github.com/rust-lang/rust / pull / 58503 , https://github.com/rust-lang/rust/pull/58581 , https://github.com/rust-lang/rust/pull/58583 , https://github.com/rust -lang / ржавчина / тянуть / 59170 , https://github.com/rust-lang/rust/pull/59355 , https://github.com/rust-lang/rust/pull/59415 , https: // GitHub .com / rust-lang / rust / pull / 60058 , https://github.com/rust-lang/rust/pull/60280 , https://github.com/rust-lang/rust/pull/60284 и большинство недавно https://github.com/rust-lang/rust/pull/59008. (Они в основном были отделены от основного запроса на перенос константных дженериков .)


В каком статусе сейчас? Некоторые тесты константных дженериков теперь работают 🎉 Однако есть еще такие, которые не работают, и есть несколько FIXME в коде, которые нам все еще нужно решить. Однако сейчас мы приближаемся, и мы находимся на стадии, когда есть некоторые низко висящие фрукты, если вы хотите помочь.

  • Во-первых, нам нужно больше тестов. Пока существует лишь несколько тестов константных дженериков , но нам бы хотелось больше. На данный момент, поскольку нам известно о ряде проблем, нам не нужны отчеты об ошибках, но нам нужно пройти тесты. Если вы придумали тестовый пример, который работает (и не слишком похож на существующий), не стесняйтесь открывать пулреквест, чтобы добавить его.
  • Во-вторых, как уже упоминалось, по всему коду разбросано несколько символов FIXME(const_generics) . Мы планируем пройти через них, но у @yodaldevoid и у меня не так много времени, поэтому, если вы думаете, что можете решить одну из них, продолжайте (хотя вы можете оставить комментарий, в котором говорится об этом, поэтому мы не делаем этого) t дублирование усилий).

Я написал обзор некоторых из оставшихся проблем реализации до того, как дженерики const будут готовы для надлежащего тестирования, в верхнем посте, чтобы дать общее представление о том, что осталось сделать.

На это ушло время, но мы стабильно добиваемся прогресса и надеемся не отставать. Это мотивирует, наконец, увидеть, как все начинает становиться на свои места!

Все 202 Комментарий

44275 добавил предикат ConstEvaluatable , а WF([T; expr]) теперь требует ConstEvaluatable(expr) , поскольку expr вычисляется лениво (даже если это просто целочисленный литерал). Для выполнения этого предиката требуется, чтобы выражение было успешно вычислено, тогда как нормализация игнорирует ошибку и просто оставляет нетронутым найденное выражение Unevaluated , что более или менее происходит со связанными проекциями типов. Я ожидаю, что та же система будет масштабироваться до константных дженериков.

@EpicatSupercell выразил заинтересованность в работе над этим, я буду наставлять их на начальном этапе реализации. Однако мы не можем зайти слишком далеко из-за ограничений, описанных в # 44275.

То есть нам нужна ленивая нормализация @nikomatsakis , чтобы позволить константным выражениям, встроенным в типы, соблюдать границы в области (из определения функции / типа / impl / и т. Д. Элемента, в котором они находятся), без создания циклических зависимостей в половине случаев.

Точки реализации (для более прямого наставничества ищите @eddyb в Gitter или eddyb в IRC):

  • Объявление / синтаксис:
  • Декларация / Семантика

    • Структуры данных: ty::Generics - добавить параметры const к типу

    • Преобразование из HIR: generics_of - создать ty::ConstParameterDef из hir::ConstParam

  • Использование / разрешение имени

    • Структуры данных: Def - добавить вариант для const параметров

    • Этап разрешения имени: with_type_parameter_rib - поддержка типов и const generics

  • Использование / семантика

    • ConstVal - добавить Param вариант, аналогичный ty::TyParam

    • Преобразование из HIR: TyArray с длиной, равной ExprPath которая разрешена в Def::ConstParam следует использовать ConstVal::Param вместо ConstVal::Unevaluated - в аналогичном мода на то, как Def::TyParam превращается в ty::TyParam

    • subst::Kind - поддержите &ty::Const и также отметьте as_const там, где отмечены as_type и as_region

  • Вывод

    • ConstVal - добавить InferVar вариант, аналогичный ty::ReVar / ty::TyInfer(TyVar(_))

    • InferCtxt - const аналоги int_unification_table и его UnifyKey impl

    • ty::relate::{Relate,TypeRelation} - поддержка, относящаяся к ty::Const , и обработка ConstVal::InferVar аналогично тому, как super_combine_tys делает это для типов.

Обратите внимание, что все это должно позволять impl<T, const N: usize> Trait for [T; N] {...} , но не передавать постоянное выражение типу / функции, например ArrayVec<T, 3> .

Я бы хотел нанести удар по этому поводу :)

@jplatte @eddyb Есть новости по этому

@samsartor перед основной работой по внедрению

@jplatte Было бы неплохо, если бы можно было связать

PR пока нет. Все мои работы можно найти здесь .

Хорошая работа @jplatte 🍻

Сейчас PR за фундамент (рефакторинг Generics ): # 45930

Я думаю, что это уже включено, но было бы хорошо обработать сворачивание стиля C ++ для обработки n-мерных массивов в стиле линейной алгебры с различной длиной, IE - размерный массив 4x4 [[f64; 4]; 4] или 4x3x5x6 размерный массив [[[[[ f64; 6]; 5]; 3]; 4] и иметь возможность правильно обертывать и генерировать специализированные методы как для него, так и для правильных реализаций признаков для скалярных векторов с правильными размерами и т. д. См. https://gist.github.com/huhlig / 8b21850b54a75254be4b093551f8c2cb для простейшего примера.

Я не припомню, чтобы кто-либо ранее предлагал выражения свертки для Rust, тем более как часть этого RFC. Но поскольку это Rust, есть ли причина, по которой мы не можем реализовать выражения свёртки как обычный макрос, а не как новый специальный синтаксис?

Что делать после объединения # 45930? @jplatte

@kjaleshire Следующим шагом является реализация синтаксического анализа константных дженериков (см. комментарий @eddyb далее). Я уже начал работу над этим, прежде чем стало ясно, что рефакторинг в рефакторинге будет необходим. Мои текущие работы по этому поводу можно найти здесь ; он еще не был перебазирован с тех пор, как PR рефакторинга был объединен.

@jplatte Я думаю, вы хотели упомянуть @kjetilkjeka.

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

@jplatte не хочет быть нетерпеливым, но проводилась ли над этим какая-нибудь работа? Вам нужна помощь? Кто-то должен помочь?

@ est31 Извините. Давно не было времени над этим поработать, и я не совсем уверен, когда у меня будет время. Может быть, кому-то еще лучше продолжить с того места, на котором я остановился. Я думаю, что часть синтаксического анализа в основном закончена. В коде есть два места, где я не был уверен, что делать в случае, если общий параметр является параметром const:

Также пока нет тестов для синтаксического анализа константных дженериков (и для красивого печатаемого кода, который я обновил в то же время). Не уверен, какую еще информацию я могу предоставить, которая будет необходима / полезна для кого-то еще, чтобы продолжить работу над этим, но не стесняйтесь пинговать меня, если что-то неясно в моем коде.

@jplatte ~ None в первом и ничего во втором ~~ (cc @pnkfelix @nikomatsakis)

РЕДАКТИРОВАТЬ : в любом случае делать нечего.

@eddyb Вы уверены, что для первого связанного фрагмента нужно вернуть None ? Я могу не понимать, что здесь происходит, но мне кажется, что ничего не надо делать. Если возвращается None другие параметры, которые могут быть небезопасными, будут пропущены.

@yodaldevoid Извините, вы правы, я не знал, что это было на Generics .

Я хотел бы продолжить с того места, где остановился @jplatte . Я работал над этим уже пару дней, просматривая инструкции наставничества

@yodaldevoid Приятно слышать. Я не знаю насчет Gitter, но вы определенно получите много рекомендаций по #rustc и / или # rust-internals в IRC!

@yodaldevoid : не могли бы вы сотрудничать? Я также занимался исследованием этой проблемы (😅) - возможно, мы могли бы заполнить пробелы в работе друг друга. (Здесь вы можете увидеть, что я сделал .) Может быть, мы могли бы поговорить об этом в IRC (#rustc или # rust-internals)?

@varkor Похоже, ты впереди меня. Я, безусловно, был бы готов к сотрудничеству. В какой-то момент я попытаюсь привлечь вас к IRC, а пока посмотрю, сделал ли я что-нибудь, чего вы еще не сделали.

Есть ли в этом прогресс?
Я пишу код для встроенного, и мне действительно нужны константные дженерики.

@ qwerty19106
В этом вопросе, хотя и медленно, наблюдается прогресс. @varkor и я работали над этим время от времени, пока у нас было время. На этой неделе я добился некоторого прогресса, и мы видим свет в конце туннеля для базового использования.

Помимо простой реализации константных дженериков, мы (varkor) сделали некоторую очистку, чтобы сделать все это возможным (см. # 48149 и # 48523). Я считаю, что текущий план состоит в том, чтобы дождаться выполнения этих двух запросов на включение, прежде чем вставлять константные дженерики, но varkor может рассказать об этом больше.

Я действительно понимаю, что вам нужны константные дженерики для встроенной работы. Я начал с этого, потому что мне тоже очень нужны константные дженерики, чтобы я мог очистить и сделать безопасными по типу большие участки встроенного кода.

TL; DR: прогресс идет, но это сложно. Я чувствую тебя на встроенном фронте.

Для флажка "документация" было бы здорово найти что-нибудь в руководстве по

Спасибо @yodaldevoid за ваш ответ. Я с нетерпением жду окончания твоей работы.

Обновление для любопытных (так как мне тоже было любопытно). По поводу двух упомянутых выше PR: # 48523 объединился, а # 48149 стабильно продвигается.

@ mark-im Хорошая штука! Хорошая работа @varkor. Вы знаете, когда примерно это расчетное время прибытия? :-)

Ура, @ flip111.

Похоже, второй большой PR рефакторинга № 48149 слился :)

/ cc меня

Далее @varkor PR # 51880

Просто хотел дать быстрое обновление, так как я знаю, что некоторые люди спрашивают о прогрессе с константными дженериками.

Чтобы дать некоторый контекст, в марте у нас с @yodaldevoid была начальная реализация, которая почти работала (большая часть реализации, казалось, была сделана, и мы просто убирали некоторые оставшиеся сбои). Однако ветка, над которой мы работали, была pre-miri. Когда miri был объединен (и в ряде последующих PR), код для работы с постоянной оценкой значительно изменился, а это означает, что многое из того, что мы делали, устарело.

Вдобавок к этому было решено, что код общих параметров необходимо очистить в целом, прежде чем добавлять константные дженерики, как для улучшения читабельности, так и для того, чтобы делать ошибки, когда мы забыли иметь дело с константными дженериками, в определенном случае труднее делать. Это было сделано в https://github.com/rust-lang/rust/pull/48523 , https://github.com/rust-lang/rust/pull/48149 и будет завершено в https: // github. ком / ржавчина-ланг / ржавчина / тянуть / 51880. Они были немного более сложными, чем я первоначально ожидал, и на их прохождение потребовалось немного больше времени, чем предполагалось.

Тем временем мы с @yodaldevoid работали над тем, чтобы наша исходная реализация была совместима со всеми последующими изменениями в rustc. Это заняло некоторое время, но мы приближаемся к цели (хотя есть извечная проблема: никогда не бывает так много времени, как вы ожидали). Я надеюсь, что скоро у нас появятся хорошие новости по этому поводу. (Между тем https://github.com/rust-lang-nursery/chalk добился значительных успехов, что должно устранить некоторые из трудностей, описанных изначально @eddyb .)


Некоторые люди спрашивают, чем они могут помочь: я думаю, что на этом этапе нам будет проще попытаться завершить начальную реализацию, а затем посмотреть, какие части требуют внимания. Как только это будет готово, нам понадобится много тестов с использованием дженериков const в разных местах (включая недопустимые для сообщений об ошибках), так что это определенно то, что мы могли бы сделать с большой помощью! Мы сообщим вам, когда это произойдет!

Извините, если это не подходящее место для этого, но у меня есть предложение относительно равенства абстрактных константных выражений. В общем, это напрямую сводится к полной зависимой типизации и неразрешимой территории. Однако в ржавчине все в конечном итоге создается с конкретными значениями / типами, поэтому мы можем утверждать некоторые равенства и отложить их проверку до мономорфных экземпляров.

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

  • автоматически обрабатывать синтаксическое равенство (т.е. N+1 == N+1 должно работать из коробки)
  • разрешить пользователю во время определения добавлять уравнения, такие как N+M == M+N (может быть, в предложении where ?). Эти уравнения могут использоваться при проверке равенства (с использованием некоторой формы замыкания сравнения). Определение, в котором не используется проверка типов с использованием этих предоставленных формул, отклоняется.
  • в точке мономорфного расширения все уравнения могут быть проверены путем фактического вычисления const exprs, которые больше не являются абстрактными. Здесь есть выбор дизайна: если уравнение сводится к false , либо может быть ошибка компиляции («уравнения - аксиомы»), либо признак не может быть реализован («уравнения - это ограничения»).
  • в параметризованной точке расширения эти уравнения переносятся: если у вас есть функция f параметризованная N где N+0==N , это уравнение не должно быть потеряно в вызывающем g так как его нужно будет проверять в каждом мономорфном месте, где вызывается g .

Плюсы этого метода в том, что в самом rustc нет необходимости в программе доказательства теорем, решателе SMT или арифметическом перезаписчике. «Только» синтаксическая проверка равенства типов, псевдонимов по модулю и набора уравнений, предоставленных пользователем.
Минусы в том, что это больше руководство для пользователя, так как кажущиеся очевидными равенства, такие как M+N=N+M должны быть добавлены явно.

Пример:

/// this works directly because of syntactic checks
fn add_end<T:Copy, const N: usize>(a: &[T;N], b: T) -> [T;N+1] {
  let x : [T;N+1] = [b;N+1];
  x[0..N] = a;
  x
}

/// this should work already
fn append<T:Copy, const M:usize, const N:usize>(a: &[T;M], b: &[T;N])
  -> [T;{M+N}]
{ … }

/// Here the equation M+M==N must be carried over or checked whenever this function is used
fn sum_pairwise_append<const M: usize, const N: usize>(a: &[i32, M],b: &[i32;N])-> [T;N]
  where M+M == N {
  let mut res : [i32; N] = append(a,a);
  for i in 0 .. N { res[i] += b[i] };
  res
} 


fn main() {
  let a: [i32; 2] = [1;2];
  let b: [i32; 4] = [2;4];
  let _ = sum_pairwise_append(a, b);  // need to check 2+2=4 
}

Уравнения как аксиомы

Это означает, что e1 == e2 всегда должно быть истинным, поэтому на самом деле это не ограничение where а больше похоже на assert_eq!(e1,e2) которое может использоваться средством проверки типов. Здесь разработчик дает обещание, что это всегда верно, и подвергает своих пользователей ошибкам компиляции, если они предоставляют параметр, опровергающий уравнение.

Уравнения как ограничения

Здесь предложение where e1 == e2 - это ограничение, которое должно быть выполнено для успешного использования этой параметризованной черты / функции. Это означает, что e1 == e2 не обязательно должно выполняться всегда, что может быть интересно для уравнений, истинных только для домена, например (x*y) / z == (y*x) / z который не может быть создан для z==0 .

@ c-cube У нас уже есть система для "неявных предложений where ", по крайней мере, для функций, производных от "правил WF (правильности)", то есть если вы определяете fn foo<'a, 'b>(x: &'a &'b i32) {} тогда он требует, чтобы вызывающие абоненты удовлетворяли 'b: 'a (в результате необходимости WF(&'a &'b i32) -> &'b i32: 'a -> 'b: 'a ).

Мы можем повторно использовать эту систему (на самом деле это уже реализовано), чтобы протолкнуть ограничения, заданные сигнатурой (в форме «это постоянное выражение успешно оценивается»), до вызывающих, тогда как все, что используется только внутри тела, все равно потребует where предложения.

Большая часть всего остального, что вы описываете, кажется близким к запланированному (включая форму «синтаксического равенства»), но нам не нужны проверки «на время мономорфизации», вместо этого мы можем иметь способы (либо неявные, либо через where clauses), чтобы «распространить ограничения на экземпляры», а затем мы создаем ошибки, когда ограничения «все еще слишком общие» (так неизвестно, если они выполняются), или в противном случае, если мы можем их оценить, а они не выполняются.

Все остальное, что вы описываете, похоже, похоже на то, что запланировано

@eddyb Знаете ли вы какие-нибудь справочные посты, в которых обсуждаются эти планы?

@ flip111 Наверное, некоторые из RFC. Может быть, @withoutboats лучше знает.

# 51880 готово: tada :: tada:

@withoutboats @ oli-obk Что вы думаете о блокировке правильного объединения выражений типа N + 1 (например, [T; N + 1] ) при получении некоторой базовой формы символьной оценки в miri ?

Я думаю, у нас уже был бы механизм для его кодирования, когда @varkor добавляет ConstValue::{Infer,Param} , мы можем использовать его для отслеживания «(поверхностно) действительных, но неизвестных» (символических) значений, а затем также включать значение (в основном целые числа) операции и вызовы в дополнение к этому.

Любые решения потока управления, отличные от assert -прежнему будут требовать известных значений, но сам assert , возможно, может быть переопределен как AssertThen(condition, assert message, success value) .
condition и success value могут быть символическими!)

Возможность экспортировать символические значения в ty::Const означает, что мы можем реализовать некоторую унификацию за пределами miri, рассматривая (большинство?) Операций как непрозрачные.

Мы должны быть осторожны, чтобы не допустить ничего из переменных вывода, но, например, для N + 1 я думаю, мы можем поддержать объединение двух Add(Param(N), Bits(1)) вместе.

@varkor Тестовый пример, который, я думаю, должен работать только с ленивой нормализацией, без унификации:

/*const*/ fn append<const N: usize, T>(xs: [T; N - 1], x: T) -> [T; N] {
    let new = MaybeUninit::<[T; N]>::uninitialized();
    unsafe {
        let p = new.as_mut_ptr() as *mut T;
        (p as *mut _).write(xs);
        p.add(N - 1).write(x);
    }
    new.into_inner()
}

Это сработает только потому, что:

  • N - 1 появляется на уровне типа ровно один раз

    • все может относиться к этому выражению непрозрачно

    • ( потенциальное решение: type ArrayWithoutLast<T, const N: usize> = [T; N - 1]; )

  • он находится в позиции аргумента в подписи

    • (не могу вспомнить, сработает ли также обратная позиция. @nikomatsakis?)

    • вызывающие абоненты могут доказать, что это WF, предоставив конкретные значения для N

    • "бурлящее" требование WF требует унификации / ArrayWithoutLast

    • в других случаях потребуется «встраивание в предложение where », например, [T; N - 1]: Sized



      • может даже злоупотреблять where [T; N - 1]: (это сегодня игнорируется? ой!)


      • снова обойти объединение с помощью where ArrayWithoutLast<T, N>: ...



В целом, мы, вероятно, можем положиться на правила WF для принудительной «проверки» константных выражений для пользователей, но нам все равно нужна унификация для других вещей (включая отсутствие необходимости в хаках вроде ArrayWithoutLast ).

53645

Небольшой вопрос, который возникает из обсуждения единиц измерения. Что нам делать, если тип generic над константой напрямую от него не зависит? Например:

struct Foo<T, const N: usize> {
    a: T,
    // Will such array be "the way" to handle this problem?
    // Or do we want some kind of `std:marker::PhantomConstData`? (which can be a simple
    // type alias to the same array)
    // Or maybe make `PhantomData` to accept constants?
    _phantom: [(), N],
}

_phantom: [(), N],

Я предполагаю, что вы имели в виду [(); N] , но это будет работать только для usize .

Я думаю, было бы разумно иметь специальный marker::PhantomConst разрешающий любой тип const .

Хм ... Перечитывая RFC с учетом последних комментариев, я задаюсь вопросом: разрешено ли что-то подобное?

struct Foo<T, const V: T> {
    _phantom: PhantomConst<T, V>,
}

Я не вижу, чтобы его где-то запретили, но я бы подумал, что он заслуживает хотя бы примера.

@Ekleog : насколько мне известно, типы параметров const могут изначально не зависеть от параметров типа (хотя это определенно имеет смысл для расширения). (Реализация будет сложнее, если мы позволим это, поэтому имеет смысл начать с простейшей версии.)

Как продвигается в этом вопросе? Знаем ли мы приблизительное время, когда это должно происходить каждую ночь?

@Zauberklavier Как только этот запрос на перенос будет выполнен. Процитирую из него:

Впереди долгий путь

@newpavlov Я предположил, что постоянные параметры будут разрешены без использования в полях.
Параметры типа причины должны использоваться из-за дисперсии, но константы не имеют этой проблемы.

@eddyb , это я тоже помню из обсуждения RFC.

@varkor Значит, PhantomConst предложенный @cuviper, не может существовать в текущем состоянии этого RFC… верно? хотя последние комментарии, похоже, указывают на то, что в любом случае нет необходимости в PhantomConst .

Влияют ли константные параметры на вариативность параметров типа, которые они включают?

В настоящее время я считаю, что константные дженерики не могут иметь параметры типа в своем типе.

Я не понимаю, что сможет сделать первая рабочая версия этого.

Например, будет ли компилироваться код в этом комментарии:
https://github.com/rust-lang/rfcs/pull/2581#discussion_r230043717

Если бы этот код был скомпилирован, это был бы способ наложить ограничения на константы перед получением синтаксиса для него.

@ rodrimati1992 Вы, вероятно, можете просто сделать (): IsTrue<{N < 128}> без отдельного типа.
Я думаю, вы хотите повторно использовать ограничение, и оно действительно работает? (потому что копирование выражения N < 128 изначально не сработает)
Думаю, вы могли бы использовать trait Lt128<const N: usize> = IsTrue<{N < 128}>; .
Я думаю, есть также возможность просто написать where [(); 128 - N], (но я не уверен, что мы гарантируем, что это вызовет панику).

@ rodrimati1992 Вы, вероятно, можете просто сделать (): IsTrue<{N < 128}> без отдельного типа.
Я думаю, вы хотите повторно использовать ограничение, и оно действительно работает? (потому что копирование выражения N < 128 изначально не сработает)
Думаю, вы могли бы использовать trait Lt128<const N: usize> = IsTrue<{N < 128}>; .
Я думаю, есть также возможность просто написать where [(); 128 - N], (но я не уверен, что мы гарантируем, что это вызовет панику).

Итак, с псевдонимами трейтов я мог бы переписать это ?:

trait AssertLessThan128<const N:usize>=
    Assert<{N<=128}, (
        Str<"uint cannot be constructed with a size larger than 128,the passed size is:",
        Usize<N>>
    ) >;

Идея с Asserttrait использует ошибку типа для печати сообщения об ошибке, встроенного в тип, что в случае AssertLessThan128:

(Str<"uint cannot be constructed with a size larger than 128,the passed size is:",Usize<N>>)

который, как я ожидаю, будет более полезным, чем where [(); 128 - N], , поскольку в сообщении об ошибке он сообщает вам, почему произошла ошибка времени компиляции.

Я думаю, вы также можете использовать if !(N < 128) { panic!("...") } в константе?

Я думал, что архитектуры std :: fmt не будет, пока не появятся ограничения const trait (или что-то подобное)?

С помощью этой вещи вы можете получать сообщения об ошибках с несколькими значениями в нем (встроенными в типы).

Может быть, лучше подождать, пока константные дженерики расскажут об этом, так как будет легче показать исполняемые примеры.

@ rodrimati1992 см. https://github.com/rust-lang/rfcs/blob/master/text/2345-const-panic.md , будет особый случай получить const panic!() прежде чем он быть возможным с помощью других функций (предположительно, сначала это не позволит настраивать форматирование значений).

@ rodrimati1992 Ага , понятно, это действительно хитрый трюк!

Какой у этого статус?

Следующим шагом по-прежнему является https://github.com/rust-lang/rust/pull/53645 AFAIK.

Это правильно. Чтобы дать быстрое обновление для тех, кто не следует PR # 53645, дженерики const компилируются и работают по крайней мере в одном простом сценарии использования. Остается доработать кодогенерацию для других вариантов использования, включая константные дженерики в arrys, и некоторую очистку вывода ошибок. После этого PR должен быть готов к слиянию и люди могут начать с ним играть.

Может быть, это не по теме, но позволит ли это варианту или вилке Chunks и связанных методов иметь Item массивом фиксированного размера времени компиляции вместо фрагмента? Это позволило бы циклу for деструктурировать / привязать к неопровержимому шаблону, который сопоставил элементы в блоке с переменными, такими как for (first, second) in arr.chunks(2) который в настоящее время не работает, и я предполагаю (без какого-либо обоснования по общему признанию), что это позволит больше оптимизировать в определенных случаях использования.

@jeffvandyke Возможно, вы думаете конкретно о ChunksExact . Такое изменение было бы нарушением API, поэтому это невозможно. Мы могли бы добавить новые API-интерфейсы, которые будут давать ссылки на массивы в будущем, но мы не можем сломать существующие.

@jeffvandyke Возможно, вы думаете конкретно о ChunksExact . Такое изменение было бы нарушением API, поэтому это невозможно. Мы могли бы добавить новые API-интерфейсы, которые будут давать ссылки на массивы в будущем, но мы не можем сломать существующие.

Я только жду, пока это будет реализовано и стабилизировано, прежде чем предлагать PR, который добавляет такой API в дополнение к ChunksExact и его вариантам :)

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

быть реализованным и стабилизированным

Я бы посоветовал не дожидаться стабилизации, так как такой API был бы еще одним полезным средством, помогающим использовать эту функцию для стабилизации.

Я предполагаю, что это еще не работает для блоков impl ? Я пытался

#![feature(const_generics)]

struct The<const Val: u64>();

impl<const Val: u64> The<Val> {
    fn the() {
        println!("{}", Val);
    }
}

но, как действительно было предупреждено, компилятор вылетел из-за

thread 'rustc' panicked at 'slice index starts at 1 but ends at 0', src/libcore/slice/mod.rs:2419:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
stack backtrace:
   0: std::sys::unix::backtrace::tracing::imp::unwind_backtrace
   1: std::sys_common::backtrace::_print
   2: std::panicking::default_hook::{{closure}}
   3: std::panicking::default_hook
   4: rustc::util::common::panic_hook
   5: std::panicking::rust_panic_with_hook
   6: std::panicking::continue_panic_fmt
   7: rust_begin_unwind
   8: core::panicking::panic_fmt
   9: core::slice::slice_index_order_fail
  10: rustc_resolve::Resolver::resolve_ident_in_lexical_scope
  11: rustc_resolve::Resolver::resolve_path
  12: rustc_resolve::Resolver::resolve_path_without_parent_scope
  13: rustc_resolve::Resolver::smart_resolve_path_fragment
  14: rustc_resolve::Resolver::smart_resolve_path
  15: <rustc_resolve::Resolver<'a> as syntax::visit::Visitor<'tcx>>::visit_ty
  16: syntax::visit::walk_generic_args
  17: syntax::visit::walk_ty
  18: rustc_resolve::Resolver::with_generic_param_rib
  19: rustc_resolve::Resolver::resolve_item
  20: rustc_resolve::Resolver::resolve_crate
  21: rustc::util::common::time
  22: rustc_interface::passes::configure_and_expand_inner
  23: rustc_interface::passes::configure_and_expand::{{closure}}
  24: <rustc_data_structures::box_region::PinnedGenerator<I, A, R>>::new
  25: rustc_interface::passes::configure_and_expand
  26: <rustc_interface::queries::Query<T>>::compute
  27: <rustc_interface::queries::Query<T>>::compute
  28: <rustc_interface::queries::Query<T>>::compute
  29: rustc_interface::queries::<impl rustc_interface::interface::Compiler>::prepare_outputs
  30: rustc_interface::interface::run_compiler_in_existing_thread_pool
  31: <std::thread::local::LocalKey<T>>::with
  32: <scoped_tls::ScopedKey<T>>::set
  33: syntax::with_globals

Ошибка связана с const в impl<const Val: u64> поскольку удаление этой части вызывает другие ошибки, но не приводит к сбою.

но поддерживает Rust даже за то, что он учел эту функцию. Я понятия не имел, сработает ли это, но синтаксис казался естественным, я пошел на это, и Лороск сказал, что он существует :)

Я не удивлен, что это не работает, так как эта горячо ожидаемая функция все еще внедряется через компилятор, пока мы говорим (см., Например, # 59008 и # 58581 и более раннюю работу над # 53645, которая была заброшена из-за слишком большого PR. , но по-прежнему оставался открытым в качестве средства отслеживания, чтобы сообщать о прогрессе).

Тем не менее, я не уверен, следует ли ожидать доступа к фрагментам за пределами границ текущих заглушек реализации. @varkor @yodaldevoid , можешь взглянуть?

Да, предупреждение верное: дженерики const еще не работают ни в какой форме. Осталось еще несколько запросов на включение, прежде чем они будут готовы к игре.

Извините, если это не то место, чтобы задавать вопросы, но я не мог найти лучшего места. Всего 2 вопроса:

  1. Может ли отдельная функция быть условно константной? Например, функция может иметь 2 подписи, например:

    const fn foo<A: const T>(x: T)  // `A` const implements `T`
    fn foo<A: T>(x: A)              // `A` implements `T` normally
    

    В этом случае foo имеет значение const iff A: const T ; если A не реализует const T , он по-прежнему удовлетворяет границе, но foo больше не является const. Также важно, чтобы автор мог указать любую общую границу для более сложных примеров (например, where A::Output : Bar ). Прекрасным примером этого является даже простая арифметика:

    // This only accepts types that const implement `T`
    const fn square_const<T: const Mul>(x: T) -> T {
      x*x
    }
    
    // This accepts types that implement `T` any way, but it is not const
    // This function behaves identically to `square_const`
    // But has a different signature and needs a different name
    fn square<T: Mul>(x: T) -> T {
      square_const(x)
    }
    
    let a: u8 = 5;
    let b: FooNumber = FooNumber::new();
    square_const(a); // `u8` const implements Mul
    square(b); // `FooNumber` implements `Mul` normally, so we need a separate function?
    

    Я твердо уверен, что определенно должен быть способ сделать это, и меня удивляет, что он не упоминается в RFC (если я его не пропустил?).

  2. _ [менее важно]: _ Будет ли способ определить в теле константной функции, работаем ли мы во время компиляции или во время выполнения? Я думаю, что какой-нибудь макрос, похожий на cfg!() был бы полезным расширением хотя бы по той причине, что отладка. cfg!() в настоящее время оценивается уже во время компиляции, поэтому я думаю (/ предполагаю), что он должен знать, используется ли функция как const или обычная функция, поскольку это тоже определяется при компиляции -время. Однако это менее важно, чем мой 1-й вопрос.

@ Кодер-256

  1. Да, см. Https://github.com/rust-lang/rfcs/pull/2632.
  2. Я не уверен, возможно ли это вообще, хотя, учитывая вышесказанное, я тоже не уверен, что это необходимо.

@ Coder-256 Оба этих вопроса не относятся к константным дженерикам, а скорее относятся к константным функциям. Дженерики констант предназначены для того, чтобы быть универсальными по сравнению с константами (например, foo<2>() ), а не для функций, которые могут быть запущены во время компиляции. Полагаю, именно поэтому вы не нашли ответов на свои вопросы в RFC 2000.

@rpjohnst Спасибо, но я думаю, что я был неясен. Я уже видел и rust-lang / rfcs # 2632, и rust-lang / rfcs # 2000, но я почти уверен, что ни в одном из них это не упоминается. (но я могу ошибаться?) Я спрашиваю об условно- константных функциях. См. Примеры, которые я написал, так как их сложно описать.

@yodaldevoid Ой , ты прав, где мне это спросить?

Что касается вопроса о макросах, я согласен с тем, что сейчас, когда я думаю об этом, в нем нет особого смысла.

Я спрашиваю об условно-константных функциях. См. Примеры, которые я написал, так как их сложно описать.

Ваше определение square_const может использоваться вместо square (то есть: оно принудительно передается функции с эквивалентной сигнатурой во время выполнения). См. Https://github.com/rust-lang/rfcs/pull/2632 для обсуждения этого поведения (кроме того, этот поток - правильное место, чтобы задавать вопросы о любых взаимодействиях между const fn и границами признаков).

@varkor Я не уверен, что это так (поскольку границы трейтов меняются), но я спрошу в rust-lang / rfcs # 2632.

Отчет о сбое:

Код:

#![feature(const_generics)]

use std::marker::PhantomData;

struct BoundedU32<const LOWER: u32, const UPPER: u32> {
    value: u32,
    _marker: PhantomData<(LOWER, UPPER)>,
}

Компилятор:

thread 'rustc' panicked at 'slice index starts at 1 but ends at 0', src/libcore/slice/mod.rs:2419:5
note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace.

error: internal compiler error: unexpected panic

note: the compiler unexpectedly panicked. this is a bug.

note: we would appreciate a bug report: https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md#bug-reports

note: rustc 1.35.0-nightly (719b0d984 2019-03-13) running on x86_64-unknown-linux-gnu

note: compiler flags: -C codegen-units=1 -C debuginfo=2 --crate-type lib

note: some of the compiler flags provided by cargo are hidden

error: Could not compile `playground`.

To learn more, run the command again with --verbose.

@Jezza : дженерики const еще не полностью реализованы и, как ожидается, не будут работать. Мы объявим, когда придет время начать экспериментировать с #![feature(const_generics)] (о чем вы получите уведомление, если вы подписаны на эту проблему).

@varkor Погодите , в примере @Jezza есть _marker: PhantomData<(LOWER, UPPER)> - это два параметра const используемые как типы, почему rustc_resolve выдает ошибку?

Хороший момент: я исследую это. Обратите внимание, что это проблема только с #![feature(const_generics)] , поэтому это не критическая проблема (использование неуниверсальных констант приводит к ошибке, как и ожидалось). Может быть, он никогда не достигнет rustc_resolve .

~ @eddyb : этот ICE исходит от resolve_ident_in_lexical_scope , поэтому я предполагаю, что он, вероятно, связан с https://github.com/rust-lang/rust/issues/58307.~

Изменить: на самом деле, может быть, нет - это, похоже, относится только к macro_rules! .

Свернуто:

#![feature(const_generics)]

struct S<const C: u8>(C);

Исправьте ошибки ICE до появления ошибки «ожидаемый тип, найденное значение».

Индекс здесь находится вне пределов:
https://github.com/rust-lang/rust/blob/master/src/librustc_resolve/lib.rs#L3919

Current nightly производит "параметр N никогда не используется" для следующего кода:

struct Foo<const N: usize> { }

Из предыдущего обсуждения я подумал, что компиляция должна принять этот код. Это просто артефакт незавершенной реализации?

Необходимо использовать параметры нормального типа PhantomData<n> ?

Думаю, уже можно сделать PhantomData<[(); N]> . Не уверен, что это то, что мы действительно хотим обеспечить, поскольку AFAIU суть PhantomData том, чтобы отмечать отклонение, а AFAIU не имеет понятия дисперсии относительно. общий параметр const.

И это работает, только когда N имеет тип usize .

Мы решили, что во время обсуждения RFC нет необходимости использовать константные параметры, и текущая реализация является ошибкой.

@withoutboats Можете ли вы указать, где в RFC это указано? Я пытался найти что-то в этом роде и, должно быть, пропустил это.

я не знаю, было ли это включено в текст RFC

@withoutboats Не

@yodaldevoid На этот комментарий ссылались ранее: https://github.com/rust-lang/rust/issues/44580#issuecomment -419576947. Но не в RFC PR, а по этому поводу.

Я не могу просматривать историю комментариев несколько лет назад, но могу объяснить: использование переменных типа требуется в качестве блокиратора, чтобы заставить вас задуматься о дисперсии этих параметров (IMO, это также не нужно, и мы могли бы по умолчанию использовать ковариантные, но это отдельная тема). Параметры Const не имеют никакого взаимодействия с дисперсией, поэтому у этого не будет мотивации.

@ HadrienG2 Спасибо, что нашли соответствующий комментарий.

@withoutboats Я действительно не ожидал, что вы

Спасибо за объяснение. Я должен признать, что я никогда не смогу осмыслить дисперсию, сколько бы раз я ни пытался ее изучить, но даже без этого, если подумать еще раз, отказ от использования параметров стоимости имеет смысл. Я добавлю исправление этой ошибки в наш список FIXME.

Вот краткий обзор достижений константных дженериков.


Прежде чем можно было бы должным образом начать работу над константными дженериками, необходимо было провести некоторый рефакторинг. @jplatte взяла на себя задачу https://github.com/rust-lang/rust/pull/45930. Затем @jplatte начал работать над основной реализацией константных дженериков, но обнаружил, что у них недостаточно времени для продолжения.

@yodaldevoid и я продолжили с того места, где остановился @jplatte , но быстро обнаружили, что прогресс https://github.com/rust-lang/rust/pull/48149 , https://github.com/rust-lang/rust/pull / 48452 , https://github.com/rust-lang/rust/pull/48523 , https://github.com/rust-lang/rust/pull/51880.

После этого можно было бы всерьез приступить к реализации константных дженериков. С тех пор мы с @yodaldevoid медленно, но верно добавляли поддержку дженериков const: https://github.com/rust-lang/rust/pull/58191 , https://github.com/rust-lang/rust / pull / 58503 , https://github.com/rust-lang/rust/pull/58581 , https://github.com/rust-lang/rust/pull/58583 , https://github.com/rust -lang / ржавчина / тянуть / 59170 , https://github.com/rust-lang/rust/pull/59355 , https://github.com/rust-lang/rust/pull/59415 , https: // GitHub .com / rust-lang / rust / pull / 60058 , https://github.com/rust-lang/rust/pull/60280 , https://github.com/rust-lang/rust/pull/60284 и большинство недавно https://github.com/rust-lang/rust/pull/59008. (Они в основном были отделены от основного запроса на перенос константных дженериков .)


В каком статусе сейчас? Некоторые тесты константных дженериков теперь работают 🎉 Однако есть еще такие, которые не работают, и есть несколько FIXME в коде, которые нам все еще нужно решить. Однако сейчас мы приближаемся, и мы находимся на стадии, когда есть некоторые низко висящие фрукты, если вы хотите помочь.

  • Во-первых, нам нужно больше тестов. Пока существует лишь несколько тестов константных дженериков , но нам бы хотелось больше. На данный момент, поскольку нам известно о ряде проблем, нам не нужны отчеты об ошибках, но нам нужно пройти тесты. Если вы придумали тестовый пример, который работает (и не слишком похож на существующий), не стесняйтесь открывать пулреквест, чтобы добавить его.
  • Во-вторых, как уже упоминалось, по всему коду разбросано несколько символов FIXME(const_generics) . Мы планируем пройти через них, но у @yodaldevoid и у меня не так много времени, поэтому, если вы думаете, что можете решить одну из них, продолжайте (хотя вы можете оставить комментарий, в котором говорится об этом, поэтому мы не делаем этого) t дублирование усилий).

Я написал обзор некоторых из оставшихся проблем реализации до того, как дженерики const будут готовы для надлежащего тестирования, в верхнем посте, чтобы дать общее представление о том, что осталось сделать.

На это ушло время, но мы стабильно добиваемся прогресса и надеемся не отставать. Это мотивирует, наконец, увидеть, как все начинает становиться на свои места!

Я месяцами ждал этого поста @varkor :) Я не мастер Rust, но я хотел бы заняться одним из FIXME s

Поздравляем @jplatte , @yodaldevoid и @varkor. Это огромный шаг к избавлению от пользовательских трейтов Array-like в математических библиотеках.

Перекрестная публикация ...

@varkor Что касается тестов, возможно, это поможет узнать, что, как ожидается, будет работать. Например, меня удивило, что это не работает: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=d84ffd15226fcffe02c102edb8ae5cf1

Также для справки тех, кто интересуется FIXME: https://oli-obk.github.io/fixmeh/

@ mark-im попробуйте поставить {} вокруг FOO . Таким образом, это сработает.

Цитата https://github.com/rust-lang/rfcs/blob/master/text/2000-const-generics.md :

При применении выражения в качестве параметра const (кроме массивов), которое не является выражением идентичности, выражение должно содержаться в блоке. Это синтаксическое ограничение необходимо, чтобы избежать необходимости бесконечного просмотра вперед при синтаксическом анализе выражения внутри типа.

{expression} необходимо только в том случае, если выражение не является идентификатором или литералом, что является ошибкой.

Цитата из того же RFC:

Выражение идентичности: выражение, которое не может быть вычислено дальше, кроме как путем замены его именами в области видимости. Сюда входят все литералы, а также все иденты - например, 3, «Hello, world», foo_bar.

@ mark-im Да, в настоящее время мы не можем отличить иденты для типов от констант при первоначальном синтаксическом анализе. Мы пробили решение о том, как со всем этим справиться в будущем. Полагаю, будущее за настоящим.

В качестве небольшой предыстории мы поговорили о двух способах, которые я могу вспомнить о том, как с этим справиться. Первый способ - заставить людей отмечать константные аргументы (либо набрав перед ними const либо каким-либо другим способом). Это не очень хорошо с точки зрения эргономики, но легко с точки зрения синтаксического анализа. Второй способ - рассматривать все общие аргументы как одинаковые до тех пор, пока мы не начнем объединять их с универсальными параметрами позже во время компиляции. Вероятно, это тот метод, который мы хотим использовать, и уже была проделана некоторая работа, чтобы сделать это возможным, мы просто еще не сделали последний шаг.

Потрясающая работа. Я рад помочь, исправив некоторые из FIXME , если смогу. У меня нет большого опыта работы с кодовой базой rustc, поэтому я начну с FIXME https://github.com/rust-lang/rust/blob/master/src/librustc_mir/monomorphize/item.rs# L397 , вроде бы лёгкий.

А как насчет следующего кода:

trait Foo {
    const N: usize;
    fn foo() -> [u8; Self::N];
}

В настоящее время это приводит к ошибке компиляции «Не найден связанный элемент с именем N для типа Self в текущей области». Будет ли такой код принят после завершения FIXME s или для его реализации потребуются дополнительные усилия?

@yodaldevoid

Быстрый вопрос, извиняюсь, если это уже обсуждалось.

Второй способ - рассматривать все общие аргументы как одинаковые до тех пор, пока мы не начнем объединять их с универсальными параметрами позже во время компиляции. Вероятно, это тот метод, который мы хотим использовать, и уже была проделана некоторая работа, чтобы сделать это возможным, мы просто еще не сделали последний шаг.

Разве это не противоречит принципу Rust, который делает сигнатуры функций явными, чтобы избежать ошибок, связанных с реализацией функции? Возможно, я совершенно не понимаю этого, и вы говорите о синтаксическом анализе общих параметров в теле функции.

Разве это не противоречит принципу Rust, который делает сигнатуры функций явными, чтобы избежать ошибок, связанных с реализацией функции? Возможно, я совершенно не понимаю этого, и вы говорите о синтаксическом анализе общих параметров в теле функции.

Речь идет об определении того, являются ли идентификаторы, передаваемые в качестве универсальных параметров функции, константами или типами.

Пример:

fn greet<const NAME:&'static str>(){
    println!("Hello, {}.",NAME);
}
const HIS_NAME:&'static str="John";
greet::<HIS_NAME>();
greet::<"Dave">();

Обратите внимание, что при определении функции вы должны указать, что NAME является константой, но когда вы ее вызываете, нет необходимости говорить, что HIS_NAME является константой внутри оператора turbofish ( ::< > ).

@zesterer Если бы это предлагаемое решение было сделано прозрачным для пользователя (например, при определении функции не было разницы между типами параметров), вы были бы правы. Однако идея предлагаемого решения заключается не в изменении поведения пользователя, а в изменении только реализации. Пользователь по-прежнему будет записывать сигнатуры функций с явными параметрами типа, констант и времени жизни, а общие аргументы все равно должны будут соответствовать соответствующему параметру, изменится только то, как мы его анализируем в компиляторе.

@newpavlov Я удивлен, что код не работает. Если бы вы могли представить отдельный выпуск, мы были бы признательны.

@yodaldevoid @robarnold

Ах, понял. Я ошибочно предположил, что это связано с сигнатурами типа / функции.

Для использования константных дженериков для встроенных типов массивов я открыл # 60466, чтобы узнать мнения других по этому поводу.

На данный момент блоки Impl кажутся полностью сломанными . Ожидается ли это в текущем состоянии функции, или мне следует открыть другой вопрос по этому поводу?

На данный момент блоки Impl кажутся полностью сломанными . Ожидается ли это в текущем состоянии функции, или мне следует открыть другой вопрос по этому поводу?

Вам нужно добавить {} s вокруг N , impl<const N: usize> Dummy<{N}> {} .

Но да, тогда вы получите ошибку о параметре без ограничений.

Ах, спасибо. Забыл про брекеты!

Я не удивлен, что он терпит неудачу, как только эта проблема решена, поскольку это был сильно свернутый тестовый пример.

... но да, этот, вероятно, должен работать:

#![feature(const_generics)]

trait Dummy {}

struct Vector<const N: usize> {
    data: [f32; N],
}

impl<const N: usize> Dummy for Vector<{N}> {}

... и все еще не работает с E0207:

warning: the feature `const_generics` is incomplete and may cause the compiler to crash
 --> src/lib.rs:1:12
  |
1 | #![feature(const_generics)]
  |            ^^^^^^^^^^^^^^

error[E0207]: the const parameter `N` is not constrained by the impl trait, self type, or predicates
 --> src/lib.rs:9:12
  |
9 | impl<const N: usize> Dummy for Vector<{N}> {}
  |            ^ unconstrained const parameter

error: aborting due to previous error

For more information about this error, try `rustc --explain E0207`.
error: Could not compile `small-matrix`.

To learn more, run the command again with --verbose.

... и я предполагаю, что это то, что @varkor имел в виду под "проблемами с обработкой массивов для дженериков consts" в # 60466:

#![feature(const_generics)]

fn dummy<const N: usize>() -> [f32; N] {
    [0.; N]
}

->

warning: the feature `const_generics` is incomplete and may cause the compiler to crash
 --> src/lib.rs:1:12
  |
1 | #![feature(const_generics)]
  |            ^^^^^^^^^^^^^^

error: internal compiler error: cat_expr Errd
 --> src/lib.rs:3:44
  |
3 |   pub fn dummy<const N: usize>() -> [f32; N] {
  |  ____________________________________________^
4 | |     [0.; N]
5 | | }
  | |_^

error: internal compiler error: cat_expr Errd
 --> src/lib.rs:4:5
  |
4 |     [0.; N]
  |     ^^^^^^^

error: internal compiler error: QualifyAndPromoteConstants: Mir had errors
 --> src/lib.rs:3:1
  |
3 | / pub fn dummy<const N: usize>() -> [f32; N] {
4 | |     [0.; N]
5 | | }
  | |_^

error: internal compiler error: broken MIR in DefId(0/0:3 ~ small_matrix[5b35]::dummy[0]) ("return type"): bad type [type error]
 --> src/lib.rs:3:1
  |
3 | / pub fn dummy<const N: usize>() -> [f32; N] {
4 | |     [0.; N]
5 | | }
  | |_^

error: internal compiler error: broken MIR in DefId(0/0:3 ~ small_matrix[5b35]::dummy[0]) (LocalDecl { mutability: Mut, is_user_variable: None, internal: false, is_block_tail: None, ty: [type error], user_ty: UserTypeProjections { contents: [] }, name: None, source_info: SourceInfo { span: src/lib.rs:3:1: 5:2, scope: scope[0] }, visibility_scope: scope[0] }): bad type [type error]
 --> src/lib.rs:3:1
  |
3 | / pub fn dummy<const N: usize>() -> [f32; N] {
4 | |     [0.; N]
5 | | }
  | |_^

thread 'rustc' panicked at 'no errors encountered even though `delay_span_bug` issued', src/librustc_errors/lib.rs:356:17
note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace.

error: internal compiler error: unexpected panic

note: the compiler unexpectedly panicked. this is a bug.

note: we would appreciate a bug report: https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md#bug-reports

note: rustc 1.36.0-nightly (cfdc84a00 2019-05-07) running on x86_64-unknown-linux-gnu

note: compiler flags: -C debuginfo=2 -C incremental --crate-type lib

note: some of the compiler flags provided by cargo are hidden

@ HadrienG2
См. № 60619 и № 60632.

Я думаю, что этот код должен строить:
https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=e45b7b5e881732ad80b7015fc2d3795c

но в настоящее время это ошибки:

ошибка [E0119]: конфликтующие реализации трейта std::convert::TryFrom<[type error]> для типа MyArray<_, _> :

К сожалению, это ограничение Try{From,Into} и не имеет ничего общего с константными дженериками. Во многих случаях они не могут быть реализованы в общем виде: манеж

@oberien в вашем примере T - это внешний тип, и неизвестно, удерживается ли T: Into<Foo<T>> или нет.

В моем примере [T; N] - это тип, определенный в libcore, и это #[fundamental] (я даже не знаю, что это значит), поэтому я думаю, что распознаватель признаков действительно знает, что [T; N]: Into<MyArray<T, {N}>> не выполняется, и там не должно быть конфликта, я думаю?

[...] и #[fundamental] [...]

Это проблема, fundamental связана с разрешением нижестоящим пользователям определять реализации трейтов, когда они переносят локальный тип в базовый тип-оболочку. На этой игровой площадке вы можете видеть, что ваша реализация работает между нефундаментальными типами, но терпит неудачу, если тип from помечен как fundamental (с гораздо лучшим сообщением об ошибке).

@varkor @yodaldevoid Итак, я только что оказался в ситуации, когда S<{N == 0}> (где S принимает параметр const bool а N a const usize ) не является ни S<{true}> ни S<{false}> в глазах компилятора (в том смысле, что он не реализует те же черты, что и оба), и я не уверен, является ли это ошибкой или ожидаемое ограничение текущего прототипа. Не могли бы вы освежить в памяти текущую логику объединения?

В качестве минимального примера это ...

// Machinery for type level if-then-else
struct Test<const B: bool>;
trait IfFn<S, Z> { type Out; }
impl<S, Z> IfFn<S, Z> for Test<{true }> { type Out = S; }
impl<S, Z> IfFn<S, Z> for Test<{false}> { type Out = Z; }

// Returns an u8 if B is true, else an u16
fn should_be_ok<const B: bool>() -> <Test<{B}> as IfFn<u8, u16>>::Out {
    0
}

... выдает следующую ошибку:

error[E0277]: the trait bound `Test<B>: IfFn<u8, u16>` is not satisfied
  --> src/lib.rs:32:1
   |
32 | / fn should_be_ok<const B: bool>() -> <Test<{B}> as IfFn<u8, u16>>::Out {
33 | |     0
34 | | }
   | |_^ the trait `IfFn<u8, u16>` is not implemented for `Test<B>`
   |
   = help: the following implementations were found:
             <Test<false> as IfFn<S, Z>>
             <Test<true> as IfFn<S, Z>>

На самом деле я не знаю, но я думаю, что проблема может заключаться в том, что у компилятора нет логики, чтобы решить, является ли литерал 0 u8 или u16 . Попробуйте что-нибудь вроде: 0u8 as _

Это не так, сообщение об ошибке остается прежним.

Но если вам нужна версия, не предполагающая вывода целочисленного типа, вот глупая минимизация:

struct Test<const B: bool>;
trait Not { const B: bool; }
impl Not for Test<{true }> { const B: bool = false; }
impl Not for Test<{false}> { const B: bool = true; }

fn should_be_ok<const B: bool>() -> bool {
    <Test<{B}> as Not>::B
}

По-прежнему не работает из-за того, что Test<{B}> якобы не реализует Not .

Привет ! Я не совсем уверен, должна ли работать унификация на константных значениях или нет, но в разработке есть новая система унификации («мел»), но это не то, что в настоящее время использует rustc.
Так что то, что вы пытаетесь сделать, возможно, придется подождать, пока не будет готов мел. И даже тогда я не уверен, что он будет работать или должен работать даже с мелом.
См. Https://github.com/rust-lang/rust/issues/48049 для получения информации о прогрессе.

@ HadrienG2 Не могли бы вы подать отдельную

@carado Unification выполняется на константах, иначе ничего из этого не сработает. Дженерики констант не блокируются мелом.

@ HadrienG2, когда у нас будет специализация для

struct Test<const B: bool>;
trait Not { const B: bool; }
impl<const B: bool> Not for Test<B> { const B: bool = false; }
impl Not for Test<{false}> { const B: bool = true; }

fn should_be_ok<const B: bool>() -> bool {
    <Test<{B}> as Not>::B
}

Что вы хотите сделать, так это провести какую-то проверку на полноту, например, на соответствие, только что расширенное до системы черт. Не знаю, чтобы об этом упоминалось ни в одной ветке.

@yodaldevoid Зарегистрировано как https://github.com/rust-lang/rust/issues/61537 .

@carado Chalk не имеет отношения к фактическим "примитивам системы типов", одним из которых является этот.
То есть rustc все еще должен (даже сегодня, поскольку Chalk уже частично интегрирован) реализовать ту часть унификации, которая имеет дело с сущностями, непрозрачными для Chalk (такими как два разных константных выражения).
Если мы реализуем это (в любом случае, у нас нет никаких планов на ближайшее будущее), это не сильно изменится даже после того, как Мел заменит систему черт (что является его основной целью, а не унификацией).

Ой, как же тогда плохо! Думаю, я действительно не понимаю, о чем говорю.

Одна из замечательных вещей, которые предоставляют константные дженерики, - это простые функции, вычисляемые во время компиляции, например, factorial.

Пример:

fn factorial<const X: i32>() -> Option<i32> {
    match X {
        i if i < 0 => None,
        0 => Some(1),
        1 => Some(1),
        i => Some(factorial::<i - 1>().unwrap() + i)
    }
}

Насколько я понимаю, есть некоторая ограниченная поддержка дженериков const в nightly? Если да, то есть ли место более организованное, чем этот выпуск, где я мог бы найти, как его использовать и все такое?

@ dancojocaru2000 const функции должны быть предпочтительным способом вычисления уровня значений во время компиляции, например https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=4994b7ca9cda0bfc44f5359443431378 , что происходит не работать, потому что они еще не полностью реализованы. Но и const универсальными.

Ваш фрагмент может быть проблематичным, потому что я не уверен, должен ли match работать с аргументами const . Вы также смешали в коде сложение с умножением, но это не имеет большого значения.

Я думаю, что вы уже можете делать факториалы во время компиляции с кодировкой Peano, которая часто отображается как демонстрация для других функциональных языков.

вычисляемые простые функции во время компиляции

Думаю, более подходящей функцией будет const fn .

Теоретически ваш код будет работать почти как есть

#![feature(const_generics)]

fn factorial<const X: i32>() -> Option<i32> {
    match X {
        i if i < 0 => None,
        0 => Some(1),
        1 => Some(1),
        i => Some(factorial::<{X - 1}>().unwrap() + i)
    }
}

fn main() {
    println!("{:?}", factorial::<10>());
}

Но это не так:

warning: the feature `const_generics` is incomplete and may cause the compiler to crash
 --> src/main.rs:1:12
  |
1 | #![feature(const_generics)]
  |            ^^^^^^^^^^^^^^

error: internal compiler error: src/librustc_codegen_ssa/mir/operand.rs:79: unevaluated constant in `OperandRef::from_const`

Хотя вам действительно стоит использовать беззнаковый тип:

#![feature(const_generics)]

fn factorial<const X: u32>() -> u32 {
    match X {
        0 => 1,
        1 => 1,
        _ => factorial::<{ X - 1 }>() + X,
    }
}

fn main() {
    println!("{:?}", factorial::<10>());
}

есть ли место более организованное, чем этот выпуск, где я могу найти, как его использовать и все такое?

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

факториалы во время компиляции с кодировкой Пеано,

В качестве примера см. Ящик typenum.

Теоретически ваш код будет работать почти как есть

При вызове factorial::<0> _ => factorial::<{X - 1}>() * X тоже будет генерироваться кодом, верно? Но попытка сделать это приведет к потере целочисленного значения.

Могу ли я ожидать, что подобный код будет компилироваться в будущем?

#![feature(const_generics)]

trait NeedsDrop<const B: bool> { }
impl<T> NeedsDrop<std::mem::needs_drop<T>()> for T { }

fn foo<D: NeedsDrop<false>>(d: D) { }

Это пересекается с недавней реализацией некоторых массивов [T; N] для N <= 32 как константных дженериков, но следующий код не компилируется на последней ( 4bb6b4a5e 2019-07-11 ) каждую ночь с различными ошибками the trait `std::array::LengthAtMost32` is not implemented for `[u64; _]`

#[derive(Copy, Clone, PartialEq, Eq)]
pub struct BigintRepresentation<
    const N: usize
>(pub [u64; N]) 
where [u64; N]: std::array::LengthAtMost32, 
   [u64; N*2]: std::array::LengthAtMost32;

хотя следующее:

#[derive(Copy, Clone, PartialEq, Eq)]
pub struct BigintRepresentation<
    const N: usize
>(pub [u64; N]) 
where [u64; N]: std::array::LengthAtMost32;

Похоже, N*2 не является допустимой константой или квалификатором для такого ограничения

@shamatar N*2 следует заключить в фигурные скобки, например `[u64; {N * 2}]

Я думаю, что ошибки «признака не реализованы» происходят из-за того, что макросы производных не добавляют границу для [u64; {N*2}] , но если производные удаляются, в настоящее время существует ICE:

Ошибка: внутренняя ошибка компилятора: константа в типе игнорировала ошибку: TooGeneric
-> src / main.rs: 5: 1
|
5 | / pub struct BigintRepresentation <
6 | | const N: использовать
7 | | > (pub [u64; N])
8 | | где [u64; N]: std :: array :: LengthAtMost32,
9 | | [u64; {N * 2}]: std :: array :: LengthAtMost32;
| | ____________________________________________ ^

поток 'rustc' запаниковал из-за 'ошибок не обнаружено, хотя delay_span_bug выдано', src / librustc_errors / lib.rs: 366: 17
примечание: запустите с переменной окружения RUST_BACKTRACE=1 чтобы отобразить обратную трассировку.

Этот код не компилируется:

#![feature(const_generics)]

struct Foo<const X: usize>([u8; X]);

impl<const X: usize> Foo<X> {
    fn new() -> Self {
        Self([0u8; X])
    }
}
warning: the feature `const_generics` is incomplete and may cause the compiler to crash
 --> src/lib.rs:1:12
  |
1 | #![feature(const_generics)]
  |            ^^^^^^^^^^^^^^

error[E0573]: expected type, found const parameter `X`
 --> src/lib.rs:5:26
  |
5 | impl<const X: usize> Foo<X> {
  |                          ^
  |                          |
  |                          not a type
  |                          help: try using the variant's enum: `regex_syntax::ast::HexLiteralKind`

error[E0107]: wrong number of const arguments: expected 1, found 0
 --> src/lib.rs:5:22
  |
5 | impl<const X: usize> Foo<X> {
  |                      ^^^^^^ expected 1 const argument

error[E0107]: wrong number of type arguments: expected 0, found 1
 --> src/lib.rs:5:26
  |
5 | impl<const X: usize> Foo<X> {
  |                          ^ unexpected type argument

error: aborting due to 3 previous errors

@npmccallum вам нужно сделать Foo<{X}>

@ pengowen123 Да, он компилируется (сбой компилятора с constant in type had an ignored error: TooGeneric ) без derive , но тогда может возникнуть отдельный вопрос, действительно ли такое "двойное ограничение", которое фактически является N <= 32 и N <= 16 не применяется компилятором.

Выражения, в которых упоминаются параметры, вероятно, не будут работать еще какое-то время, [T; N] и Foo<{N}> имеют специальный регистр, чтобы в них не было выражений с упоминанием N , а скорее напрямую обращаться к параметру N , минуя более крупную проблему.

Использование Self вызывает сбой.
Даже поменяв его на Value<{C}> , он все равно вылетает.

#![feature(const_generics)]

struct Value<const C: usize>;

impl<const C: usize> Value<{C}> {
    pub fn new() -> Self {
        unimplemented!()
    }
}

pub fn main() {
    let value = Value::new();
}

То же, что и # 61338, источником проблемы является инкрементная компиляция.

вычисляемые простые функции во время компиляции

Думаю, более подходящей функцией будет const fn .

Теоретически ваш код будет работать почти как есть

#![feature(const_generics)]

fn factorial<const X: i32>() -> Option<i32> {
    match X {
        i if i < 0 => None,
        0 => Some(1),
        1 => Some(1),
        i => Some(factorial::<{X - 1}>().unwrap() + i)
    }
}

fn main() {
    println!("{:?}", factorial::<10>());
}

Но это не так:

warning: the feature `const_generics` is incomplete and may cause the compiler to crash
 --> src/main.rs:1:12
  |
1 | #![feature(const_generics)]
  |            ^^^^^^^^^^^^^^

error: internal compiler error: src/librustc_codegen_ssa/mir/operand.rs:79: unevaluated constant in `OperandRef::from_const`

Хотя вам действительно стоит использовать беззнаковый тип:

#![feature(const_generics)]

fn factorial<const X: u32>() -> u32 {
    match X {
        0 => 1,
        1 => 1,
        _ => factorial::<{ X - 1 }>() + X,
    }
}

fn main() {
    println!("{:?}", factorial::<10>());
}

Эта функция мне тоже пригодится, const fn недостаточно. Я хочу использовать его для N-мерного массива с конечным условием измерений = 0.

Я могу создать структуру:

#![feature(const_generics)]
struct MyArray<T: Default, const len: usize> {
    real_array: [T; len]
}

impl<T: Default, const len: usize> MyArray<T, {len}> {
    fn new() -> Self {
        return MyArray {
            real_array: [Default::default(); len]
        }
    }
}

Это не удается, потому что я не могу инициализировать массив из-за:

error: array lengths can't depend on generic parameters
 --> src/main.rs:9:46
  |
9 |             real_array: [Default::default(); len]
  |                                              

Я попытался обойти это, установив для значений const общее значение.

Это проходит мимо вышеуказанной ошибки, но затем компилятор аварийно завершает работу.

error: internal compiler error: src/librustc/ty/subst.rs:597: const parameter `height/#0` (Const { ty: usize, val: Param(height/#0) }/0) out of range when substituting substs=[]

Языку отчаянно нужны базовые универсальные константы, так что стандартной библиотеке не нужно определять каждую функцию для каждого размера массива. Это единственная причина, по которой я не использую ржавчину. Нам действительно нужны полные функции времени компиляции по Тьюрингу?
Возможно, я ошибаюсь, но простых целочисленных выражений должно быть достаточно, и я надеюсь, что никто не зря тратит свое время на то, чтобы убедиться, что эти дурацкие примеры работают.

Языку отчаянно нужны базовые константы-дженерики, так что стандартной библиотеке не нужно определять каждую функцию для каждого размера массива.

Там уже есть какие-то усилия https://github.com/rust-lang/rust/issues/61415.

Надеюсь, никто не зря тратит время на то, чтобы убедиться, что эти дурацкие примеры работают.

Некоторые люди, я не вижу в этом никаких проблем;)

Это единственная причина, по которой я не использую ржавчину.

Это наименее интересная причина, которую я видел для тех, кто не использует ржавчину. Вы уверены, что говорите правду?

но простых целочисленных выражений должно хватить

Сделать даже такой уменьшенный прицел невероятно сложно. Попробуйте свои силы, у нас есть действительно способные люди, которые делают это, и есть причина, по которой это занимает так много времени.

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

Надеюсь, никто не зря тратит время на то, чтобы убедиться, что эти дурацкие примеры работают.

Никто не знает, miri уже намного мощнее, чем C ++ constexpr .
И никто не работает над чем-то необычным, вроде получения N = M из N + 1 = M + 1 .

Большинство этих ошибок связано не с выражениями в них, а с системой типов и тем, как универсальные шаблоны const взаимодействуют со всеми другими функциями.
Они все еще были бы там, если бы все, что у вас было, было универсальными const и целочисленными литералами.

Кстати, я думаю, что ошибка «длина массива не может зависеть от общих параметров» для [expr; N] выражений не нужна, мы могли бы использовать тот же трюк, что и для типов [T; N] , и вытащить из N не оценивая его как выражение.

Хотя я действительно хочу нанести удар по этому поводу, я не уверен, что я тот человек. Я использовал немного ржавчины и очень мало знаю теорию компиляторов. Мне может понадобиться немного наставничества, но я, безусловно, готов. 😄

Изменить: у меня есть некоторый опыт работы с программным обеспечением в целом.

@varkor , я искал что-нибудь полезное на rustc , и я хотел бы подойти и помочь. Кроме того, спасибо за откровенность в отношении вашей способности уделять этому время!

@varkor , я на самом деле следую за вышеприведенным комментарием, так что если у вас есть какой-либо совет, я весь уши! Не стесняйтесь направить меня и на другой канал связи.

Я только что понял, что мы можем сделать прямо сейчас, это разрешить выражениям Foo::<{...}> / [expr; ...] ссылаться на общие параметры (в части ... ).

Это потому , что выражения должны быть вложенным где - то внутри тела, и , как правило , для предотвращения циклических зависимостей.

Однако меня беспокоит, что наличие, например, [(); [0; 1][0]] в подписи может нарушить работу, поэтому нам, вероятно, все равно понадобится пробег кратера (а в настоящее время это занимает вечность).

Для всех, кто заинтересован в помощи с константными дженериками, я бы посоветовал взглянуть на список открытых проблем константных дженериков и исследовать то, что вам кажется интересным. Некоторые уже провели расследование, которое должно быть в комментариях. Вероятно, придется немного покопаться в большинстве проблем. Если вы планируете что-то расследовать, полезно оставить комментарий, чтобы мы не дублировали усилия (но вы можете часто игнорировать ответственного за проблему, если какое-то время не было никаких действий). Если у вас есть вопросы, вы можете задать их в комментариях к проблеме, на Discord или Zulip. @eddyb , @yodaldevoid , @ oli-obk и я знакомы со многими из соответствующих областей и хорошие люди, чтобы спросить. Спасибо за проявленный интерес!

cc @hameerabbasi , @ranweiler

Вопросы о константных дженериках:

  1. Где найти документацию (чтобы мне не приходилось здесь задавать вопросы)
  2. Какие ограничения существуют на данный момент при использовании параметра const произвольного типа?
  3. Какие важные функции дженериков const не реализованы / вызывают сбой компилятора?

PS (Соавторы :) Большое спасибо за работу над этой функцией.

const-generics все еще находится в разработке, поэтому официальных документов по нему еще нет. Я не уверен насчет двух других вопросов. Когда я в последний раз использовал const-generic, они разбились, когда я указал некоторые параметры const-generic, но прошел почти месяц с тех пор, как я что-то с ним делал в последний раз, поэтому с тех пор все могло измениться.

И никто не работает над чем-то необычным, вроде получения N = M из N + 1 = M + 1 .

Было бы очень полезно иметь решатель для таких ограничений типа. Например, при добавлении двух N-битных чисел размер возврата должен быть (N + 1), чтобы учесть бит переполнения. Когда (например) даны два 5-битных числа, решающая программа должна проверить, что N + 1 = 6 . Надеюсь, позже это можно будет прикрутить к константным дженерикам :)

@ flip111 Да, я думаю, что планируется добавить это позже, но такого рода ограничения общего выражения очень сложны и их трудно реализовать. Так что мы можем не увидеть их по крайней мере несколько лет.

Честно говоря, решение подобных проблем, связанных с ограничениями, очень похоже на работу для Prolog. Может быть, вариант с двигателем для мела - вариант? Но ведь это решает черты характера, не так ли?

Кстати, мне нравится эта тема, хотя я не могу позволить себе помочь с этим банкоматом. Спасибо всем, кто работает над Const Generics, за ваше время и приверженность. 💐

Небольшое обновление: я продвигаюсь вверх по дереву Rust. Я действительно планирую внести свой вклад, но хочу заниматься самообучением там, где мне не нужно чрезмерное обучение.

Я могу создать структуру:

#![feature(const_generics)]
struct MyArray<T: Default, const len: usize> {
    real_array: [T; len]
}

impl<T: Default, const len: usize> MyArray<T, {len}> {
    fn new() -> Self {
        return MyArray {
            real_array: [Default::default(); len]
        }
    }
}

Это не удается, потому что я не могу инициализировать массив из-за:

error: array lengths can't depend on generic parameters
 --> src/main.rs:9:46
  |
9 |             real_array: [Default::default(); len]
  |                                              

Я столкнулся с той же проблемой, но нашел обходной путь с помощью MaybeUninit :
https://play.rust-lang.org/?version=nightly&mode=release&edition=2018&gist=3100d5f7a4efd844954a6fa5e8b8c526
Очевидно, что это просто обходной путь для получения правильно инициализированных массивов, но для меня этого достаточно, пока не будет доступен правильный способ.
Примечание: я думаю, что код всегда должен работать так, как задумано, но если кто-то обнаружит ошибку с использованием небезопасного кода, я буду рад ее исправить.

@raidwas вы = для инициализации неинициализированной памяти. Сделайте это вместо этого,

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=5d962ce7c553e850030240244608ec00

@KrishnaSannasi, спасибо, хороший улов: D (технически я не сбрасывал неинициализированную память, так как использую ее только для примитивов, но хорошо иметь здесь правильную версию)

Технически сбросить даже числа с плавающей запятой не определено, но сейчас это невозможно использовать.

Технически сбросить даже числа с плавающей запятой не определено, но сейчас это невозможно использовать.

Я ожидал, что удаление любого типа Copy , даже неинициализированного, будет бесполезным. Может, это стоит изменить.

Технически сбросить даже числа с плавающей запятой не определено, но сейчас это невозможно использовать.

Я ожидал, что удаление любого типа Copy , даже неинициализированного, будет бесполезным. Может, это стоит изменить.

Технически это все еще UB. Хотя безопасное удаление определенных значений Copy является операцией, компилятор может решить выполнить некоторые неожиданные оптимизации, если вы попытаетесь удалить неинициализированную память любого типа (например, удалить весь код, который когда-либо мог коснуться этого значения), что может что-то сломать. Это мое понимание.

Не будет невежливым, но ≥ 60 человек получают уведомления о комментариях в этой ветке, поэтому нам, вероятно, следует свести обсуждение не по теме к минимуму. Давайте лучше воспользуемся этим, чтобы координировать работу над константными дженериками, поскольку это то, чего мы все хотим.

Ошибка при использовании _aliases, указывающих на тип с параметром const-param_ из другого ящика (зависимость).

Например:

  • ящик A, lib
    `` #! [crate_type = "lib"]

    ! [функция (const_generics)]

Тип паба Псевдоним= Структура;
pub struct Struct(Т);

- crate B

внешний ящик crate_a;
используйте crate_a :: Alias;

pub fn inner_fn (v: Псевдоним) {}
`` ''
аварийный журнал (44580) .txt

@ fzzr- звучит как # 64730

Также звучит как https://github.com/rust-lang/rust/issues/61624

Я переключил код на константные дженерики, и похоже, что на самом деле есть два разных варианта использования. Я не уверен, следует ли их объединять или нам лучше использовать два разных синтаксиса для вариантов использования.

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

ИМХО, мы должны формально использовать «константные аргументы» вместе с константными дженериками, чтобы люди не писали мутантный код, вводя тысячу «константных дженериков» (по одному на каждый аргумент), чтобы компилятор оценивал определенные переменные как литералы / константы.

@mqudsi Не могли бы вы привести пример? Уже есть планы и продолжаются фундаментальные работы, чтобы сделать const eval более мощным. Однако это ортогонально константным дженерикам.

Я имею в виду, если вы хотите реорганизовать типичный код парсера, например, для повторного использования:

let mut result: u32 = 0;
let mut digits = 0;
while digits < max_digits && src.has_remaining() {
    match src.get_u8() {
        d<strong i="6">@b</strong>'0'..=b'9' => {
            result = result*10 + (d - b'0') as u32;
            digits += 1;
        },
        b'>' => match result {
            0..=191 => break, // valid range
            _ => return Err(DecoderError::OutOfRange),
        },
        _ => return Err(DecoderError::Malformed)
    }
}

Обычно вы перемещаете его в функцию, за исключением того, что следующее не будет работать, потому что определенные значения, которые используются при сопоставлении с образцом, стали переменными:

#[inline(always)]
fn read_str_digits<B: bytes::buf::Buf>(src: &mut B, stop_word: u8, 
    max_digits: u8, min_value: u32, max_value: u32) -> Result<u32, DecoderError> {
    let mut result: u32 = 0;
    let mut digits = 0;
    while digits < max_digits && src.has_remaining() {
        match src.get_u8() {
            d<strong i="10">@b</strong>'0'..=b'9' => {
                result = result*10 + (d - b'0') as u32;
                digits += 1;
            },
            stop_word => match result {
                min_value..=max_value => break, // valid range
                _ => return Err(DecoderError::OutOfRange),
            },
            _ => return Err(DecoderError::Malformed)
        }
    }

    ...
}

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

#[inline(always)]
fn read_str_digits<const MinValue: u32, const MaxValue: u32, const StopWord: u8, B: bytes::buf::Buf>
(src: &mut B, max_digits: u8) -> Result<u32, DecoderError> {
    let mut result: u32 = 0;
    let mut digits = 0;
    while digits < max_digits && src.has_remaining() {
        match src.get_u8() {
            d<strong i="14">@b</strong>'0'..=b'9' => {
                result = result*10 + (d - b'0') as u32;
                digits += 1;
            },
            StopWord => match result {
                MinValue..=MaxValue => break, // valid range
                _ => return Err(DecoderError::OutOfRange),
            },
            _ => return Err(DecoderError::Malformed)
        }
    }

    ...
}

На сегодняшний день даже этот код не будет работать, потому что компилятор не обнаруживает, что общие значения const являются константами, допустимыми для использования в шаблоне, но это, очевидно, неверно и должно быть рассмотрено до того, как появится RFC 2000. Не поймите меня неправильно, я годами боролся за общие константы, и у меня есть PR, готовые пойти на дюжину основных ящиков (не могу дождаться, чтобы сделать часовой пояс в chrono константным универсальным и унифицировать все различные типы DateTime ), но, я думаю, если станет возможным злоупотреблять константными дженериками для подделки константных аргументов (где под «константой» мы на самом деле подразумеваем «буквальный»), тогда вы увидите широкое распространение злоупотребление этим.

Это не обязательно конец света, но, не копаясь слишком глубоко в нем, кажется, что правильная и полная реализация константных дженериков в любом случае обязательно будет включать в себя всю сантехнику для константных аргументов, так что мы могли бы также занять дополнительное время доработать синтаксис / ux / story для аргументов const, пока мы на нем, и избежать неудачной эры зловония кода. Да, все вышеперечисленное можно сделать с помощью макросов, но эргономика дженериков const в тысячу раз проще.

fwiw, вот как я могу представить версию аргумента const:

#[inline(always)]
fn read_str_digits<B: Bytes>(src: &mut B, min_value: const u32, 
    max_value: const u32, stop_word: const u8, max_digits: u8) -> Result<u32, ()> {
    let mut result: u32 = 0;
    let mut digits = 0;
    while digits < max_digits && src.has_remaining() {
        match src.get_u8() {
            d<strong i="23">@b</strong>'0'..=b'9' => {
                result = result*10 + (d - b'0') as u32;
                digits += 1;
            },
            stop_word => match result {
                min_value..=max_value => break, // valid range
                _ => return Err(()),
            },
            _ => return Err(())
        }
    }

    ...
}

Он был бы практически идентичен дженерикам const, если бы не семантика.

Обычно вы перемещаете его в функцию, за исключением того, что следующее не будет работать, потому что определенные значения, которые используются при сопоставлении с образцом, стали переменными:

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

@mqudsi Простите меня, если это глупый вопрос, но я не вижу ничего плохого в приведенном вами примере. Для меня это выглядит как совершенно допустимый вариант использования дженериков const: определение, обобщенное для работы с произвольными значениями, такими как max / min. Я действительно не вижу преимуществ константных аргументов над константными дженериками. Мне они кажутся равносильными; то есть константные аргументы могут быть просто реализованы как десугаринг до константных дженериков. Не могли бы вы подробнее рассказать о том, что не так с этим шаблоном проектирования?

Вот краткое изложение работы над дженериками const с момента последнего обновления . В прошлый раз все было связано с основной реализацией: убедиться, что все совмещено, и пройти некоторые из основных тестовых примеров. С тех пор усилия были сосредоточены на том, чтобы сделать константные дженерики более надежными: исправление случаев, которые приводили к сбою компилятора или не работали неожиданно, или улучшали диагностику. Мы приближаемся к чему-то, что работает надежно, хотя есть еще путь.


  • @ skinny121 за последний месяц проделал фантастическую работу, исправляя различные нерешенные проблемы с дженериками const:

    • Расширение набора тестов (https://github.com/rust-lang/rust/pull/60550)

    • Устранение проблем с выводом типа (https://github.com/rust-lang/rust/pull/64679, https://github.com/rust-lang/rust/pull/65579)

    • Поддержка большего количества типов в константных дженериках, включая строки и срезы (https://github.com/rust-lang/rust/pull/64858) и указатели (https://github.com/rust-lang/rust/pull/64986 ) (хотя обратите внимание, что они, вероятно, не будут немедленно стабилизированы вместе с остальными дженериками const, поскольку они не поддерживаются в исходном RFC )

    • Устранение диагностических проблем с помощью константных дженериков (https://github.com/rust-lang/rust/pull/65154, https://github.com/rust-lang/rust/pull/65579)

    • Исправление ряда проблем с использованием кросс-ящика констант дженериков (https://github.com/rust-lang/rust/pull/65365)

  • @eddyb исправил некоторые проблемы с
  • @matthewjasper исправил некоторые проблемы с использованием
  • @davidtwco исправила проблему, связанную с дженериками и конструкторами const (https://github.com/rust-lang/rust/pull/60839).
  • @GuillaumeGomez исправил отображение дженериков констант в Rustdoc (https://github.com/rust-lang/rust/pull/61605)
  • @varkor исправил некоторые проблемы с выводом типа (https://github.com/rust-lang/rust/pull/61570, https://github.com/rust-lang/rust/pull/60742, https://github.com/rust-lang/rust/pull/61570, https://github.com/rust-lang/rust/pull/60742, https://github. com / rust-lang / rust / pull / 60508), проблема, ограничивающая места, где могут использоваться обобщенные константы (https://github.com/rust-lang/rust/pull/60717) и различные сбои компилятора (https: / /github.com/rust-lang/rust/pull/61380, https://github.com/rust-lang/rust/pull/61333, https://github.com/rust-lang/rust/pull/60710 )

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

Мы также начали использовать константные дженерики внутри самого компилятора: реализации типажей массивов теперь используют константные дженерики благодаря работе @scottmcm (https://github.com/rust-lang/rust/pull/60466, https : //github.com/rust-lang/rust/pull/62435), что привело к повышению производительности , а также позволит нам не ограничивать реализации свойств массивов в будущем (когда константные дженерики стабилизируются). @Centril использовал тот же подход для улучшения VecDeque (https://github.com/rust-lang/rust/pull/63061).


Многие из наиболее часто встречающихся ошибок с дженериками const были исправлены за последние несколько месяцев. @nikomatsakis исследует ленивую нормализацию , которая должна исправить множество оставшихся проблем, в то время как @jplatte изучает устранение неоднозначности параметров const из параметров типа (так что вам больше не придется вводить {X} для аргумент const).

Я также хочу поблагодарить @eddyb за их наставничество и рецензирование дженериков const на протяжении всего процесса разработки, что было бесценно.

Есть еще немало других проблем, которые необходимо решить, и, как и прежде, мы могли бы использовать всю помощь, которую можем получить - если вы заинтересованы в решении любой из оставшихся проблем , не стесняйтесь связаться со мной по этой проблеме или Discord или Zulip за советы по началу работы.

@mqudsi @ mark-im Было бы уместно расширить текущую синтаксическую альтернативу для дженериков, impl Trait в позиции аргумента, до константных дженериков? Это синтаксис @mqudsi, предлагаемый для этого

Лично я думаю, что это улучшит читаемость таких вариантов использования, но я вижу одну проблему: для передачи данных используются обычные дженерики, поэтому они привязаны к аргументу. Это не относится к константным дженерикам, так как бы вы их передали? Притворяться аргументами?
В случае impl Trait в позиции аргумента его также нельзя использовать с синтаксисом turbofish. Для const в аргументе будет наоборот. Это может сбивать с толку.

Я не уверен, перевешивают ли преимущества здесь "странность", но я все равно хотел поделиться своими мыслями.

Я смутно вспомнил, как обсуждали это раньше, а теперь нашел: https://internals.rust-lang.org/t/pre-rfc-const-function-arguments/6709

@ PvdBerg1998, это кажется очень плохой идеей. Я действительно не совсем понимаю, что синтаксис <…> - это трудный для чтения синтаксис. Здесь две возможности:

  1. Вы думаете, как и я, что это нетрудно читать, даже с несколькими границами.
  2. Вы думаете, что это трудно читать: тогда, может быть, решением было бы поместить их в предложение where ?

Теперь сравните с предварительным RFC, указанным выше. Это будет введение _tag_ (в смысле системы типов) непосредственно в позицию аргументов функции, то есть привязки переменных для тела функции.

Другими словами: это очень сбивает с толку и, как некоторые люди заявили для impl Trait в позиции arg, в этом нет необходимости. Пожалуйста, не повторяйте одну и ту же ошибку дважды, чтобы добавить функцию, которая того не стоит. Особенно, если вы «притворяетесь аргументами». Ржавчина здесь пойдет не в том направлении. Да, синтаксис должен быть легче, но не более запутанным. Пожалуйста остановись.

@phaazon
Я лично полностью согласен со связанным предложением о том, что разрешение констант в позиции аргумента будет действительно хорошим эргономическим улучшением. Речь идет не только о сигнатурах функций (возможно, предложения where вообще не «решают» проблему, но затрудняют понимание подписей, поскольку вам нужно анализировать 3 места вместо одного), но и о как эти функции будут использоваться. Сравнивать:

let r = _mm_blend_ps::<{2 * C}>(a, b);
let r = _mm_blend_ps(a, b, 2 * C);

Вторая строка, на мой взгляд, гораздо естественнее второй. Другой пример - создание матрицы: MatrixF32::new(3, 3) vs. MatrixF32::new::<3, 3>() . Я категорически не согласен с тем, что эта функция будет "очень запутанной". Для пользователя это не более чем дополнительное требование к аргументу функции. Мы уже можем эмулировать константные аргументы с помощью макросов (см. Внутренние функции SIMD внутри std ), но это довольно уродливо и неэффективно.

Что касается impl Trait в позиции аргумента, сначала я тоже был против этой функции, но через некоторое время передумал и считаю, что в итоге это было хорошее решение. Единственная незавершенная часть сейчас - это взаимодействие с явными параметрами типа, предоставляемыми через turbofish. Я думаю, что хорошим решением было бы сделать impl Trait arguments невидимыми для turbofish, то есть пользователи должны использовать его, когда они уверены, что явное устранение неоднозначности типа не потребуется. Это позволит нам значительно снизить потребность в _ внутри турбофиш в некоторых сценариях.

Думаю, обсуждение аргументов const здесь не по теме, поэтому, вероятно, не стоит его здесь продолжать.

Одна вещь, которая отделяет const arguments от impl Trait arguments (о котором упоминал @ PvdBerg1998 ), заключается в том, что они являются аргументами со всем, что подразумевается для вывода типа.

Вы не можете передать сам тип в качестве аргумента, но можете передать значение, и вполне разумно позволить функции вывести свои общие аргументы const из своих обычных аргументов в скобках.

Это довольно часто встречается в C ++, например:

template <typename T, size_t N>
size_t length(T (&array)[N]) {
    return N;
}

Это немного отличается от простого принятия параметра константного и desugaring его константного родового параметр ( fn foo(const N: usize) -> fn foo<const N: usize>() ). Это ближе к ограничению параметра универсальным параметром const и последующему выводу этого универсального аргумента const из аргумента. Таким образом, приведенный выше пример матрицы может выглядеть так:

impl<const W: usize, const H: usize> MatrixF32<W, H> {
    fn new(w: usize<W>, h: usize<H>) -> Self { .. }
}

... где usize<X> - это выдуманный синтаксис для " usize со значением X ." (Аналогично, вам может понадобиться usize<X..Y> для ранжированных типов, которые обсуждались в контексте обобщения NonZero .)

Если вы хотите избежать привязки к ранжированным типам, вы можете взглянуть на языки с зависимым типом, которые позволяют использовать параметры непосредственно в типах с небольшой настройкой области видимости:

fn new(const W: usize, const H: usize) -> MatrixF32<W, H> { .. }

Это не аргументы. Это дженерики . На уровне системы типов это означает, что их _значения_ живут во время компиляции, в то время как значения аргументов функции живут во время выполнения. Смешивание того и другого крайне ошибочно.

Поскольку мне небезразличны системы типов, это предложение противоречит многим разумным и здравым принципам. Вы не хотите смешивать значения и типы. Потому что да, N (обратите внимание, я не сказал usize , я сказал N ), даже если вы думаете, что это значение, больше похоже на тип чем значение. В качестве обоснования, Haskell не имеет констант-дженериков, но имеет DataKinds , позволяющий поднимать обычные конструкторы данных как типы:

foo :: Integer -> Integer
foo 0 -- 0 has type Integer

-- but
data P (a :: Integer)

type MyP = P 10 -- 10 has kind Integer, which “value” is the 10 type

Так:

fn foo<const X: usize>()

Здесь X больше похоже на тип, чем на значение.

Также меня волнует мономорфизация. Если я прочитаю это:

let x = foo(12, "Hello, world!", None);

В вашем предложении, если мы посмотрим на определение foo , трудно понять, какие аргументы собираются монорфизировать foo . Поскольку каждый раз, когда вы передаете другое значение для общего константы, вы создаете полностью новую функцию.

Может быть, это кажется более интуитивным по _вам_ и _вашим причинам_, но у меня также есть основания сказать, что это вообще не интуитивно понятно с точки зрения правильности типов. Заявление, что они являются аргументами, похоже на утверждение, что параметризованные функции в математике имеют свои параметры-аргументы. Вы путаете параметры и аргументы, демонстрируя, насколько плоха эта идея.

Возможно, вы неправильно меня прочитали, я специально сказал « const arguments ... являются аргументами », а не « const generics». Мой первый пример на Rust также точно эквивалентен DataKinds ( usize<N> - это версия N ). Здесь нет проблемы с точки зрения правильности типов - я спорю не с точки зрения интуиции, а по аналогии с существующими, устоявшимися и надежными системами типов.

Что касается вашего беспокойства по поводу мономорфизации - это не отличается от сегодняшнего дня! Каждый аргумент потенциально может вызвать новую мономорфизацию. Я бы также добавил, что решение этой проблемы состоит в том, чтобы снизить стоимость всех дженериков за счет совместного использования независимого кода между экземплярами, в том числе и особенно путем преобразования константных дженериков в значения времени выполнения, когда это возможно.

(И я совершенно озадачен вашим утверждением, что я путаю параметры и аргументы, я старался их различать.)

Я понимаю, что вы имеете в виду, но все еще очень обеспокоен. Поскольку _аргумент_ помечен _const_, это означает, что вы не можете передать его _a значение_, говоря во время выполнения. Вам нужно передать ему _constant value_, которое будет отображаться на _const generic_. Между прочим, вы не упомянули об этом здесь, но это всего лишь приложение _синглетного типа_, если вы рассматриваете 10 как тип: его единственное возможное значение - 10 .

Поскольку я был против impl Trait в позиции arg, я тоже против этого (и вы поймете, почему мы можем сравнивать здесь). Несколько моментов:

  1. Я не думаю, что это необходимо, пока _const generics_ добавляют много значений.
  2. Поскольку они сродни типам, они должны принадлежать тому месту, где расположены типы, а не переменным.
  3. Я действительно хотел бы знать, почему некоторые люди думают, что <…> сложнее, чем (…) , особенно учитывая struct vs fn . В настоящее время в struct есть только <…> а в fn есть оба, потому что для них обоих можно задать параметры.
  4. Что мне действительно не нравилось в impl Trait в позиции arg, так это то, что мне было труднее увидеть, для чего функция действительно устанавливает параметры. Когда вы читаете foo<A, B> , вы знаете, что есть два параметра, которые будут соответствовать определению и реализации функции.

Я чувствую, что ты хочешь здесь делать. foo(1, 3) тебе больше foo<3>(1) чем foo(1, 3) должен принимать второй аргумент как основанный на времени выполнения, а предложение, которое вы даете, запрещает это. Я могу видеть приложения, особенно с массивами, но в настоящее время мне не нравится, как они выглядят. Я был бы намного лучше для решения, которое гласит: «Если аргумент равен const , мы можем вывести переменную типа / const generic.

fn foo<const N: usize>(a: usize, b: usize | N);

Это просто воображаемый синтаксис, который я придумал, чтобы объяснить свою точку зрения: если вы передадите b в качестве константного значения, N = b и вы получите красивый синтаксис:

foo(1, 3)

Если b не является значением const, необходимо явно передать N :

foo<3>(1, x)

Кроме того, каков будет синтаксис для этого с вашим предложением:

fn foo<const N: usize>(x: [f32; N]);
fn new(const W: usize, const H: usize) -> MatrixF32<W, H> { .. }

Это приводит нас к вопросу о связанных элементах, короче new() - это связанная функция и должна быть связана с конкретным типом, общие элементы const имеют семантику вроде «Этот тип имеет этот параметр ...», то есть зависимый тип. Итак, new() в контексте не должно иметь аргументов. Использование Explict должно выглядеть как Type<32>::new() . И, конечно же, все аргументы константных дженериков должны быть исключены.

Я чувствую, что ты хочешь здесь делать. foo (1, 3) кажется вам лучше, чем foo <3> (1), но не мне. Причина этого в том, что foo (1, 3) должен принимать второй аргумент как основанный на времени выполнения, а предложение, которое вы даете, запрещает это.

Это не моя мотивация и не намерение - я не говорю о том, что «кажется лучше», и я не считаю, что различие между компиляцией и временем выполнения должно быть объединено с различием на уровне типа / значения. Эти два элемента уже разделены - const items (value или fn) определенно не являются типами!

Все, что я понимаю, - это семантическая связь, которую вывод типа проводит между параметрами уровня значения и параметрами уровня типа. Нет никакого отношения к impl Trait - моя цель, опять же, заключалась в том, чтобы отличать аргументы const от impl Trait , именно для того, чтобы предотвратить такого рода непродуктивную тревогу.

Кроме того, каков будет синтаксис для этого с вашим предложением:

fn foo<const N: usize>(x: [f32; N]);

Этот синтаксис не изменится! Я не предлагаю удалять константные параметры с уровня типа (синтаксически или семантически), просто мы добавляем их к уровню значения, частично для облегчения вывода параметров уровня типа.

На cppcon был разговор о параметрах функции constexpr, что похоже на то, о чем говорит @ PvdBerg1998 , и похоже на внутренний поток, с которым связан @ mark-im. Кажется, там его хорошо приняли.

Это кажется хорошей идеей, но ее следует проработать после того, как мы получим начальную реализацию для констант-дженериков.

CppCon 2019: Дэвид Стоун - Удаление метапрограммирования из C ++, часть 1 из N: параметры функции constexpr

Изменить: об этом упоминалось в этом потоке, вот соответствующая статья для параметров функции constexpr
https://github.com/davidstone/isocpp/blob/master/constexpr-parameters.md

Я хотел бы попросить, чтобы мы зарезервировали обсуждение такого значительного синтаксического изменения либо для темы о внутреннем устройстве, либо для проблемы (или изменения PR) в репозитории RFC. Раздел комментариев к этой проблеме уже довольно длинный, и рассматриваемый синтаксис кажется ненужным для полезного MVP константных дженериков.

Если бы мы могли переместить комментарии, мы бы, вероятно, это сделали. Мы могли бы захотеть заблокировать проблему для членов команды @ rust-lang и скрыть все оффтопные комментарии?

Все обсуждения дизайна должны происходить в репозитории RFC, а все отчеты об ошибках должны быть в отдельных выпусках.

cc @ rust-lang / moderation Есть ли прецедент для этого?

Вы можете _конвертировать_ комментарий к проблеме, но не можете перемещать комментарии. Блокировка в порядке, и я бы просто спрятал комментарии по теме.

С началом нового года я подумал, что было бы неплохо дать еще одну обновленную информацию о том, где мы сейчас находимся с константными дженериками. Прошло чуть больше 2 лет с тех пор, как RFC был принят для [1] , [2] .


За последние пару месяцев с момента последнего обновления был достигнут хороший прогресс.

  • @ skinny121 :

    • Исправлена ​​проблема с нарушением обобщения констант при инкрементной компиляции и между ящиками (https://github.com/rust-lang/rust/pull/65652).

    • Исправлена ​​ошибка вывода типов, влияющая на константные дженерики в диагностике (https://github.com/rust-lang/rust/pull/65579)

    • Реорганизован интерфейс для вычисления констант (https://github.com/rust-lang/rust/pull/66877)

  • @yodaldevoid реализовал устранение неоднозначности параметров типа и {} (https://github.com/rust-lang/rust/pull/66104)
  • @eddyb исправил использование ленивой нормализации (https://github.com/rust-lang/rust/pull/66883)
  • @varkor :

    • Реализована проверка structural_match для запрета использования типов с настраиваемой проверкой равенства в качестве константных обобщений (https://github.com/rust-lang/rust/pull/65627)

    • Исправлена ​​некоторая диагностика, чтобы учесть константные дженерики (https://github.com/rust-lang/rust/pull/65614)

    • Выполнены различные рефакторинги и исправление вывода типов (https://github.com/rust-lang/rust/pull/65643, https://github.com/rust-lang/rust/pull/65660, https://github.com/rust-lang/rust/pull/65643, https://github.com/rust-lang/rust/pull/65660, https://github. com / ржавчина-ланг / ржавчина / тянуть / 65696)

Большое спасибо всем, кто помогал с дженериками const!


Что дальше? Самым большим препятствием для дженериков констант на данный момент является ленивая нормализация , которая требуется для определенных типов константных родовых границ (например, в массивах ). @ skinny121 в настоящее время изучает ленивую нормализацию, продолжая свои фантастические усилия по устранению серьезных ошибок в дженериках const. Это решило бы многие из текущих проблем с константными дженериками.

Если отбросить ленивую нормализацию, есть еще немалое количество ошибок и вырезок, которые мы хотели бы исправить. Если вы заинтересованы в решении какой-либо из оставшихся проблем, не стесняйтесь связаться со мной по этой проблеме или в Discord или Zulip, чтобы получить указания по началу работы. Я уверен, что мы сможем добиться хорошего прогресса в 2020 году и, надеюсь, приблизимся к моменту, когда стабилизация станет жизнеспособным обсуждением!

Есть ли подмножество дженериков const, которое не затрагивает ленивую нормализацию и не имеет большого количества ошибок и вырезок из бумаги, но которое может выражать большое количество полезного кода? Я замечаю, что стандартные признаки многих свойств для массивов работают нормально. Может быть, есть сужение, которое позволило бы другим ящикам писать такие имплименты, которые есть у нас в std, для их собственных черт, даже если они не поддерживают все более причудливые функции?

Прошло уже больше половины года, поэтому я кратко резюмирую проделанную работу. За последние 6 месяцев было много людей, участвовавших в улучшении поддержки дженериков const, поэтому спасибо всем, кто чем-то помог! Я особенно хочу поблагодарить @ skinny121 и @lcnr , которые взяли на себя некоторые большие функции, которых не хватало дженерикам const в течение некоторого времени: ленивая нормализация для констант и поддержка аргументов const в вызовах методов , а также исправление множества сложных ошибок. Их усилия очевидны из приведенного ниже резюме.

Константные дженерики теперь используются в нескольких местах компилятора, и уже проводятся эксперименты с трейтами и функциями, использующими константные дженерики, например, slice::array_chunks и IntoIterator и FromIterator для [T; N] (https://github.com/rust-lang/rust/pull/65819, https://github.com/rust-lang/rust/pull/69985). Ограничение длины 32 для реализаций признаков массива также, наконец, готовится к удалению .

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


  • @ skinny121 :

    • Реализована первая версия ленивой нормализации (https://github.com/rust-lang/rust/pull/68505, https://github.com/rust-lang/rust/pull/69181, https://github. com / rust-lang / rust / pull / 67717, https://github.com/rust-lang/rust/pull/67890)

    • Повышена производительность (https://github.com/rust-lang/rust/pull/68118)

    • Исправлены различные ошибки (https://github.com/rust-lang/rust/pull/68143)

  • @lcnr :

    • Добавлена ​​поддержка явных константных обобщений в аргументах типа для методов (https://github.com/rust-lang/rust/pull/74113)

    • Завершена первая реализация ленивой нормализации для констант (https://github.com/rust-lang/rust/pull/71973) с помощью @ skinny121

    • Добавлен линт для unused_braces (https://github.com/rust-lang/rust/pull/70081)

    • Исправлена ​​проблема с константными обобщениями в шаблонах (https://github.com/rust-lang/rust/pull/70562)

    • Запретить dyn Trait для общих параметров const (https://github.com/rust-lang/rust/pull/71038)

    • Улучшена красивая печать (https://github.com/rust-lang/rust/pull/72052)

    • Исправлены различные ошибки (https://github.com/rust-lang/rust/pull/70032, https://github.com/rust-lang/rust/pull/70107, https://github.com/rust- lang / rust / pull / 70223, https://github.com/rust-lang/rust/pull/70249, https://github.com/rust-lang/rust/pull/70284, https://github.com/rust-lang/rust/pull/70249, https://github.com/rust-lang/rust/pull/70284, https://github.com/github. com / rust-lang / rust / pull / 70319, https://github.com/rust-lang/rust/pull/70541, https://github.com/rust-lang/rust/pull/72066, https: //github.com/rust-lang/rust/pull/74159)

    • Рефакторинг (https://github.com/rust-lang/rust/pull/70478)

    • Добавлены тесты (https://github.com/rust-lang/rust/pull/74392, https://github.com/rust-lang/rust/pull/74445)

  • @eddyb :

    • Исправлены проблемы с константными обобщениями в длинах массивов (https://github.com/rust-lang/rust/pull/70452)

    • Улучшена красивая печать (https://github.com/rust-lang/rust/pull/71232)

    • Улучшенная обработка ошибок (https://github.com/rust-lang/rust/pull/71049)

    • Рефакторинг (https://github.com/rust-lang/rust/pull/70164)

  • @ Aaron1011 : исправлена ​​ошибка с использованием дженериков
  • @petrochenkov : исправлены проблемы с разрешением имен и константными дженериками (https://github.com/rust-lang/rust/pull/70006)
  • @yodaldevoid : исправлена ​​проблема с использованием времени жизни в общих параметрах const (https://github.com/rust-lang/rust/pull/74051)
  • @contrun : исправлен ICE (https://github.com/rust-lang/rust/pull/69859)
  • @ oli-obk: рефакторинг (https://github.com/rust-lang/rust/pull/69981)
  • @Centril : улучшенная диагностика (https://github.com/rust-lang/rust/pull/70261)
  • @DutchGhost : добавлен тест (https://github.com/rust-lang/rust/pull/70539)
  • @varkor :

    • Улучшенная красивая печать (https://github.com/rust-lang/rust/pull/67909, https://github.com/rust-lang/rust/pull/68111)

    • Исправлены различные ошибки (https://github.com/rust-lang/rust/pull/67906, https://github.com/rust-lang/rust/pull/68388, https://github.com/rust- lang / rust / pull / 68434)

    • Улучшенная диагностика (https://github.com/rust-lang/rust/pull/70845)

    • Добавлены тесты (https://github.com/rust-lang/rust/pull/68312, https://github.com/rust-lang/rust/pull/70939)


Хотя дженерики const уже прошли долгий путь, все еще есть ошибки и острые углы . Если вы заинтересованы в решении какой-либо из оставшихся проблем, не стесняйтесь связаться со мной, @eddyb по этой проблеме или на Zulip, чтобы получить началу работы . Спасибо!

Была ли эта страница полезной?
0 / 5 - 0 рейтинги