b0fh
b0fh

Reputation: 1735

Does rust support const-generic types with a runtime-determined value?

Consider the classic example of a const-generic datastructure: a square matrix.

struct Matrix<T, const N: usize> {
    inner: [[T; N]; N]
}

I'd like to return a structure whose const parameter is dynamically defined:

fn read_matrix() -> ??? {
    let n: usize = todo!() // the N is decided dynamically
    let r: Box<Matrix<u8, {n}>> = todo!();
    r
}

but:

I can work around the second limitation by defining a trait:

trait DynamiMatrix {
   fn size(&self) -> usize;
   fn some_method(&self);
   ...
}

impl<T, const N: usize> DynamicMatrix for Matrix<T,N> {
   fn size(&self) -> usize { N };
   fn some_method(&self){ ... }
   ...
}

But, for construction, the best I can try is:

fn read_array_pair() -> Box<dyn DynamicMatrix> {
  let n: usize = todo!();
  let m = Box::new(Matrix { inner: [[0; n]; n] });
  todo!(); // fill up m
  m
}

and Rust will still complain that n is not constant.

Is there any way to achieve this? If possible without falling back to nested Vecs, because i'd like my square invariant to be enforced?

Upvotes: 4

Views: 2687

Answers (1)

E_net4
E_net4

Reputation: 29983

Const parameters were deliberately designed so that they are always known at compile time. Just like most information about types in a program, the compiler will erase them without keeping this information at run-time. So the short answer is no, and it is unlikely that a direct means of specifying this kind of const parameter at run-time will ever be featured.

However, there are known workarounds to creating generic structures that may either contain compile-time or run-time information, and they even predate const parameters.

Consider this simplified definition of ndarray::ArrayBase:

pub struct ArrayBase<S, D> {
    /// Data buffer / ownership information. (If owned, contains the data
    /// buffer; if borrowed, contains the lifetime and mutability.)
    data: S,
    /// The lengths of the axes.
    dim: D,
    /// The element count stride per axis.
    strides: D,
}

This definition is parameterized over its source of elements S as well as its dimensionality D. D, often implemented through the primitive Dim, will then be generic over both situations:

  • For a fixed number of dimensions such as in Array2, D = Ix2, enabling the user to pass a [usize; 2] to index an element in the array.
  • If the number of dimensions is unknown at compile time, there is ArrayD, in which D = IxDyn and users can pass anything which Deref's to a [usize] for indexing.

The conclusion is that you may be interested in changing the design of your structs and traits so that these details (whether inferred at compile-time or at run-time) are encoded in a type parameter instead of a const parameter.

Upvotes: 5

Related Questions