将编译时整数类型与动态类型组合在一起

时间:2017-03-21 00:25:42

标签: rust typenum

我在Rust中使用typenum来为我正在使用的某些类型添加编译时维度检查。我想将它与动态类型结合起来,以便在给定两个不兼容的typenum类型的情况下,编译时具有不匹配维度的表达式将失败,但如果一个或多个类型为{{{},则编译正常并在运行时失败1}}。这可能在Rust吗?如果是这样,我将如何组合无符号和动态?

Dynamic

2 个答案:

答案 0 :(得分:3)

您可以继续使用Vec<T>作为最佳效果,并使用Vector<T, N>检查长度向量。为了实现这一点,您可以定义一个用于添加的特征,并为两种类型的矢量的不同组合实现它:

trait MyAdd<T> {
    type Output;
    fn add(&self, other: &T) -> Self::Output;
}

impl <T, N: Unsigned> MyAdd<Vector<T, N>> for Vector<T, N> {
    type Output = Vector<T, N>;
    fn add(&self, other: &Vector<T, N>) -> Self::Output {
        Vector::new(/* implement addition here */)
    }
}

impl <T, N: Unsigned> MyAdd<Vec<T>> for Vector<T, N> {
    type Output = Vector<T, N>;
    fn add(&self, other: &Vec<T>) -> Self::Output {
        Vector::new(/* implement addition here */)
    }
}

impl <T> MyAdd<Vec<T>> for Vec<T> {
    type Output = Vec<T>;
    fn add(&self, other: &Vec<T>) -> Self::Output {
        Vec::new(/* implement addition here */)
    }
}

impl <T, N: Unsigned> MyAdd<Vector<T, N>> for Vec<T> {
    type Output = Vector<T, N>;
    fn add(&self, other: &Vector<T, N>) -> Self::Output {
        Vector::new(/* 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]);
    let vector4_dynamic = vec![1, 2, 3, 4];

    vector3.add(&vector4); // Compile error!
    vector3.add(&vector4_dynamic); // Runtime error on length assertion
}

您可以使用内置的std::ops::Add来避免创建自己的特征,但是您无法为Vec实现它。 .add的左侧始终必须为Vector<E, N>Vec仅限于参数。您可以使用另一个Vec “newtype”包装来解决这个问题,类似于您对Vector<E, T>所做的操作,但没有长度检查和幻像类型。

答案 1 :(得分:3)

遗憾的是,

Specifying a default for type parameters仍未稳定,因此您需要使用夜间编译器才能使以下工作正常运行。

如果您正在使用默认类型参数,请注意编译器将首先尝试根据使用情况推断类型,并且只有在没有足够信息时才会回退到默认值。例如,如果要将使用显式N声明的向量和不使用N声明的向量传递给add,编译器将推断第二个向量的N必须与第一个向量的N相同,而不是为第二个向量的Dynamic选择N。因此,如果大小不匹配,则在构造第二个向量时会发生运行时错误,而不是在将它们一起添加时。

可以为不同的类型参数集定义多个impl块。例如,我们可以在new时实现N: Unsigned,在NDynamic时实现另一个。

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,
        }
    }
}

但是,这种方法有两个impl提供new方法,但对于默认类型参数不起作用;在调用new时,编译器会抱怨歧义而不是推断默认值。因此,我们需要定义一个统一N: UnsignedDynamic的特征。此特征将包含一种方法,可帮助我们正确执行new中的断言,具体取决于大小是固定的还是动态的。

#![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,
        }
    }
}

为了支持add接收固定和动态矢量,但不支持不同长度的固定矢量,我们需要引入另一个特征。对于每个N: Unsigned,只有N本身和Dynamic才会实现该特征。

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!()
}

如果您实际上不需要支持使用固定和动态向量调用add,那么您可以大幅简化:

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!()
}