I am using `typenum`

in Rust to add compile-time dimension checking to some types I am working with. I would like to combine it with a dynamic type so that an expression with mismatched dimensions would fail at compile time if given two incompatible `typenum`

types, but compile fine and fail at runtime if one or more of the types is `Dynamic`

. Is this possible in Rust? If so, how would I combine Unsigned and Dynamic?

```
extern crate typenum;
use typenum::Unsigned;
use std::marker::PhantomData;
struct Dynamic {}
// N needs to be some kind of union type of Unsigned and Dynamic, but don't know how
struct Vector<E, N: Unsigned> {
vec: Vec<E>,
_marker: PhantomData<(N)>,
}
impl<E, N: Unsigned> Vector<E, N> {
fn new(vec: Vec<E>) -> Self {
assert!(N::to_usize() == vec.len());
Vector {
vec: vec,
_marker: PhantomData,
}
}
}
fn add<E, N: Unsigned>(vector1: &Vector<E, N>, vector2: &Vector<E, N>) {
print!("Implement addition here")
}
fn main() {
use typenum::{U3, U4};
let vector3 = Vector::<usize, U3>::new(vec![1, 2, 3]);
let vector4 = Vector::<usize, U4>::new(vec![1, 2, 3, 4]);
// Can I make the default be Dynamic here?
let vector4_dynamic = Vector::new(vec![1, 2, 3, 4]);
add(&vector3, &vector4); // should fail to compile
add(&vector3, &vector4_dynamic); // should fail at runtime
}
```

Specifying a default for type parameters has, sadly, still not been stabilized, so you'll need to use a nightly compiler in order for the following to work.

If you're playing with defaulted type parameters, be aware that the compiler will first try to infer the types based on usage, and only fall back to the default when there's not enough information. For example, if you were to pass a vector declared with an explicit `N`

and a vector declared without `N`

to `add`

, the compiler would infer that the second vector's `N`

must be the same as the first vector's `N`

, instead of selecting `Dynamic`

for the second vector's `N`

. Therefore, if the sizes don't match, the runtime error would happen when constructing the second vector, not when adding them together.

It's possible to define multiple `impl`

blocks for different sets of type parameters. For example, we can have an implementation of `new`

when `N: Unsigned`

and another when `N`

is `Dynamic`

.

```
extern crate typenum;
use std::marker::PhantomData;
use typenum::Unsigned;
struct Dynamic;
struct Vector<E, N> {
vec: Vec<E>,
_marker: PhantomData<N>,
}
impl<E, N: Unsigned> Vector<E, N> {
fn new(vec: Vec<E>) -> Self {
assert!(N::to_usize() == vec.len());
Vector {
vec: vec,
_marker: PhantomData,
}
}
}
impl<E> Vector<E, Dynamic> {
fn new(vec: Vec<E>) -> Self {
Vector {
vec: vec,
_marker: PhantomData,
}
}
}
```

However, this approach with two `impl`

s providing a `new`

method doesn't work well with defaulted type parameters; the compiler will complain about the ambiguity instead of inferring the default when calling `new`

. So instead, we need to define a trait that unifies `N: Unsigned`

and `Dynamic`

. This trait will contain a method to help us perform the assert in `new`

correctly depending on whether the size is fixed or dynamic.

```
#![feature(default_type_parameter_fallback)]
use std::marker::PhantomData;
use std::ops::Add;
use typenum::Unsigned;
struct Dynamic;
trait FixedOrDynamic {
fn is_valid_size(value: usize) -> bool;
}
impl<T: Unsigned> FixedOrDynamic for T {
fn is_valid_size(value: usize) -> bool {
Self::to_usize() == value
}
}
impl FixedOrDynamic for Dynamic {
fn is_valid_size(_value: usize) -> bool {
true
}
}
struct Vector<E, N: FixedOrDynamic = Dynamic> {
vec: Vec<E>,
_marker: PhantomData<N>,
}
impl<E, N: FixedOrDynamic> Vector<E, N> {
fn new(vec: Vec<E>) -> Self {
assert!(N::is_valid_size(vec.len()));
Vector {
vec: vec,
_marker: PhantomData,
}
}
}
```

In order to support `add`

receiving a fixed and a dynamic vector, but not fixed vectors of different lengths, we need to introduce another trait. For each `N: Unsigned`

, only `N`

itself and `Dynamic`

will implement the trait.

```
trait SameOrDynamic<N> {
type Output: FixedOrDynamic;
fn length_check(left_len: usize, right_len: usize) -> bool;
}
impl<N: Unsigned> SameOrDynamic<N> for N {
type Output = N;
fn length_check(_left_len: usize, _right_len: usize) -> bool {
true
}
}
impl<N: Unsigned> SameOrDynamic<Dynamic> for N {
type Output = N;
fn length_check(left_len: usize, right_len: usize) -> bool {
left_len == right_len
}
}
impl<N: Unsigned> SameOrDynamic<N> for Dynamic {
type Output = N;
fn length_check(left_len: usize, right_len: usize) -> bool {
left_len == right_len
}
}
impl SameOrDynamic<Dynamic> for Dynamic {
type Output = Dynamic;
fn length_check(left_len: usize, right_len: usize) -> bool {
left_len == right_len
}
}
fn add<E, N1, N2>(vector1: &Vector<E, N1>, vector2: &Vector<E, N2>) -> Vector<E, N2::Output>
where N1: FixedOrDynamic,
N2: FixedOrDynamic + SameOrDynamic<N1>,
{
assert!(N2::length_check(vector1.vec.len(), vector2.vec.len()));
unimplemented!()
}
```

If you don't actually need to support calling `add`

with a fixed and a dynamic vector, then you can simplify this drastically:

```
fn add<E, N: FixedOrDynamic>(vector1: &Vector<E, N>, vector2: &Vector<E, N>) -> Vector<E, N> {
// TODO: perform length check when N is Dynamic
unimplemented!()
}
```