Alternatives to simple const generic expression

I am working on project which requires development of a spatial mesh data structure. I need to be able to support meshes of 2- or 3-dimensional elements with corresponding 1- or 2-dimensional interfaces which join neighboring elements. Every element in the mesh is guaranteed to be represented by geometries which share a common spatial dimension.

Using the knowledge that if I have an element of dimension N, the dimension of the interface will be N - 1, and the fact that N will only over be 2 or 3, I am looking for a way to represent the spatial dimensions N and N - 1 without have to revert to the nightly compiler and enabling the generic_const_exprs feature.

Currently the mesh structure looks something akin to

use nalgebra::Point;

pub struct Mesh<const N: usize> {
    points: Vec<Point<f64, N>>,
    elements: Vec<ElementData>,
    interfaces: Vec<InterfaceData>,

I have need to sometimes transfer down to the N - 1 dimension. For example, I may need to represent interfaces in their true dimension as in the following as_parametric function

// `N` is the mesh/element dimension
struct SomeInterfaceType<'a, const N: usize> {
    points: &'a [Point<f64, N>],

impl<'a, const N: usize> SomeInterfaceType<'a, N> {
    /// Transfer a point on the interface to a `N - 1`-dimensional parametric coordinate.
    /// Returns `None` if `point` is not on the interface.
    // requires `feature(generic_const_exprs)`
    fn try_to_parametric(&self, point: &Point<f64, N>) -> Option<Point<f64, { N - 1 }>>{
        // ...

I am aware of the possibility to represent the spatial dimension with a generic type instead of a const generic by using the typenum and nalgebra crates. However, in this case, every Point object would become an OPoint which would require including a DefaultAllocator: Allocator<f64, D> bound everywhere the dimension type is used which can add unwanted technical debt when applied throughout the code base.

Currently, I am using a combination of const generics and generic types to solve this problem, as in

use nalgebra::{Const, DimName, DimNameSub, U1};

trait Dim: DimName + DimNameSub<U1> {}
impl Dim for Const<2> {}
impl Dim for Const<3> {}

// e.g. LessOne<Const<3>> == Const<2>
type LessOne<D> = <D as DimNameSub<U1>>::Output;

Can anyone imagine a more succinct way of representing the spatial dimension?

Thanks for your help and for reading my wall of text!

If you only have a handful of possibilities (3d or 2d say), you could make the lower dimension another const parameter and use macros to only implement methods that make sense. Maybe use the aliases to avoid retyping the const parameters too much.

Thanks for your response! I am interested in implementing traits and methods for the general case; there isn't really any functionality that exists for 2D that wouldn't exist for 3D. Unfortunately, this seems to make using macros not an option :confused:.

Using a second const generic may work, though. The end user would have to interact with type aliases, e.g. pub type Mesh3 = Mesh<3, 2> which may be a path forward.

I think I am going to go down the path that will require the least amount of refactoring once the generic_const_exprs feature is stabilized (years from now?) which I think might be my current approach: use const generics for types and traits which don't need to do arithmetic on the const generic and generics for types and traits that do.

How so?

Another approach which may work for you:

This exploits the fact that const parmeters in associated types can be "pattern matched".


Another option might be taking advantage of From/Into: playground link

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.