Я хотел бы написать функцию, которая принимает два входных аргумента, которые имеют const общие аргументы A и B в своих типах, и которая возвращает тип, который имеет сумму A+B как общий аргумент const. Вот что я пытаюсь сделать:

pub struct X<const A: usize>();

pub fn f<const A: usize, const B: usize>(_: X<A>, _: X<B>) -> X<A + B> {
    X()
}

К сожалению, компилятор кричит на меня следующим сообщением об ошибке:

error[E0404]: expected trait, found const parameter `A` 
--> <source>:3:65  
  | 
3 | pub fn f<const A: usize, const B: usize>(_: X<A>, _: X<B>) -> X<A + B> {  
  |                                                                 ^ not a trait

error[E0404]: expected trait, found const parameter `B` 
--> <source>:3:69
  |
3 | pub fn f<const A: usize, const B: usize>(_: X<A>, _: X<B>) -> X<A + B> {
  |                                                                     ^ not a trait

error[E0747]: type provided when a constant was expected 
--> <source>:3:65
  |
3 | pub fn f<const A: usize, const B: usize>(_: X<A>, _: X<B>) -> X<A + B> {
  |                                                                 ^^^^^

error: aborting due to 3 previous errors

Я нахожу сообщения об ошибках довольно запутанными. Если я поставлю фигурные скобки вокруг A + B, сообщение об ошибке изменится на это:

error: generic parameters may not be used in const operations 
--> <source>:3:66
  |
3 | pub fn f<const A: usize, const B: usize>(_: X<A>, _: X<B>) -> X<{A + B}> {
  |                                                                  ^ cannot perform const operation using `A`
  |  = help: const parameters may only be used as standalone arguments, i.e. `A`

error: generic parameters may not be used in const operations 
--> <source>:3:70
  |
3 | pub fn f<const A: usize, const B: usize>(_: X<A>, _: X<B>) -> X<{A + B}> {
  |                                                                      ^ cannot perform const operation using `B`
  |  = help: const parameters may only be used as standalone arguments, i.e. `B`

Что кажется немного лучше, но я не понимаю, почему добавление A и B здесь запрещено.

Итак, мои два вопроса:

  • Почему здесь запрещено добавлять A и B?
  • Есть ли способ обойти это ограничение?

Я проверил на https://doc.rust-lang.org. /reference/items/generics.html#const-generics и там я могу найти ограничение, которое

константные параметры могут появляться только как отдельный аргумент внутри выражения повторения типа или массива.

Но нет намека на то, почему существует это ограничение или как его обойти.

0
Ralph Tandetzky 19 Ноя 2022 в 08:39

1 ответ

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

Вариант 1: используйте функцию generic_const_exprs:

#![feature(generic_const_exprs)]

pub struct X<const A: usize>();

pub fn f<const A: usize, const B: usize>(_: X<A>, _: X<B>) -> X<{A + B}> {
    X()
}

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

Вариант 2: используйте третий общий параметр:

pub struct X<const A: usize>();

pub fn f<const A: usize, const B: usize, const C: usize>(_: X<A>, _: X<B>) -> X<C> {
    assert_eq!(A + B, C);
    X()
}

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

1
Aiden4 19 Ноя 2022 в 10:39