I am looking to create something similar to the dimensioned crate using a concept similar to what is offered by the typenum crate. Because I only need a small piece of the functionality of both crates, I am re-implementing a simplified version of them for my own team. The point is to keep track of dimensions of mathematical computations at compile-time. I have created type aliases for common dimensions.
The problem I am running into is that my IDEs (RustRover and VSCode) provide type hints using the original types rather than the aliased ones-- see screenshots. For example, the type hint for my_time
is Dimensions<Z0, P1, Z0>
instead of Time
, etc. I understand this behavior is the pragmatic one: there could be more than one alias pointing to the same type, so there might be a possible ambiguity. But in may case this will not happen.
What options (hacks / forking are acceptable as last resort) do I have to manually indicate to my IDEs what hint to display for the inlays? I am happy to create a dedicated mapping from Dimensions<...>
to the alias for the IDEs to use.
Here is a (somewhat) minimal working example of what I'm trying to do:
use std::marker::PhantomData;
use std::ops::{Add, Mul, Sub};
trait TypeNumAdd<N2> {
type Output;
}
struct N1;
struct Z0;
struct P1;
impl TypeNumAdd<N1> for Z0 {
type Output = P1;
}
impl TypeNumAdd<N1> for P1 {
type Output = Z0;
}
impl TypeNumAdd<Z0> for N1 {
type Output = N1;
}
impl TypeNumAdd<Z0> for Z0 {
type Output = Z0;
}
impl TypeNumAdd<Z0> for P1 {
type Output = P1;
}
impl TypeNumAdd<P1> for N1 {
type Output = Z0;
}
impl TypeNumAdd<P1> for Z0 {
type Output = P1;
}
pub struct Null;
#[repr(transparent)]
pub struct Dimensions<L, T, M, Ind = Null>(
f64,
PhantomData<(L, T, M, Ind)>
);
impl<L, T, M, Ind> From<f64> for Dimensions<L, T, M, Ind> {
fn from(value: f64) -> Self {
Dimensions(value, PhantomData)
}
}
impl<L, T, M, Ind> Copy for Dimensions<L, T, M, Ind> {}
impl<L, T, M, Ind> Clone for Dimensions<L, T, M, Ind> {
fn clone(&self) -> Self {
Dimensions(self.0, PhantomData)
}
}
impl<L, T, M, Ind> Add for Dimensions<L, T, M, Ind> {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Dimensions(<f64 as Add>::add(self.0, rhs.0), PhantomData)
}
}
impl<L, T, M, Ind> Sub for Dimensions<L, T, M, Ind> {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
Dimensions(<f64 as Sub>::sub(self.0, rhs.0), PhantomData)
}
}
impl<L1, T1, M1, Ind,
L2, T2, M2,
L3, T3, M3> Mul<Dimensions<L2, T2, M2, Null>> for Dimensions<L1, T1, M1, Ind>
where
L2: TypeNumAdd<L1, Output=L3>,
T2: TypeNumAdd<T1, Output=T3>,
M2: TypeNumAdd<M1, Output=M3>
{
type Output = Dimensions<L3, T3, M3, Ind>;
fn mul(self, rhs: Dimensions<L2, T2, M2, Null>) -> Self::Output {
Dimensions(<f64 as Mul>::mul(self.0, rhs.0), PhantomData)
}
}
type Speed = Dimensions<P1, N1, Z0>;
type Length = Dimensions<P1, Z0, Z0>;
type Time = Dimensions<Z0, P1, Z0>;
#[test]
fn my_test() {
let my_time = Time::from(6.53);
let my_speed = Speed::from(1.2);
let result = my_time * my_speed;
}