我在Rust中使用typenum
来为我正在使用的某些类型添加编译时维度检查。我想将它与动态类型结合起来,以便在给定两个不兼容的typenum
类型的情况下,编译时具有不匹配维度的表达式将失败,但如果一个或多个类型为{{{},则编译正常并在运行时失败1}}。这可能在Rust吗?如果是这样,我将如何组合无符号和动态?
Dynamic
答案 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
,在N
为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,
}
}
}
但是,这种方法有两个impl
提供new
方法,但对于默认类型参数不起作用;在调用new
时,编译器会抱怨歧义而不是推断默认值。因此,我们需要定义一个统一N: Unsigned
和Dynamic
的特征。此特征将包含一种方法,可帮助我们正确执行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!()
}