有没有一种简单的方法可以一次转换整个标量值元组?

Is there an easy way to cast entire tuples of scalar values at once?

我想将 (u16, u16) 转换为 (f32, f32)。这是我试过的:

let tuple1 = (5u16, 8u16);
let tuple2 = tuple1 as (f32, f32);

理想情况下,我想避免写作

let tuple2 = (tuple1.0 as f32, tuple1.1 as f32);

没有, 那里 是 没有。

不,你不能。这大致相当于 "can I cast all the fields in a struct to different types all at once?".

可以写一个通用的扩展特征,它可以为你做这个转换,唯一的问题是我不相信有任何现有的通用"conversion"特征它还定义了 u16 -> f32 实现。

如果您真的想要一个执行此操作的函数,这里有一个您可以构建的尽可能最小的框架:

trait TupleCast<T> {
    type Output;
    fn tuple_cast(self) -> <Self as TupleCast<T>>::Output;
}

impl<T> TupleCast<T> for () {
    type Output = ();
    fn tuple_cast(self) -> <() as TupleCast<T>>::Output {
        ()
    }
}

impl<S, T> TupleCast<T> for (S,) where S: CustomAs<T> {
    type Output = (T,);
    fn tuple_cast(self) -> <(S,) as TupleCast<T>>::Output {
        (self.0.custom_as(),)
    }
}

impl<S, T> TupleCast<T> for (S, S) where S: CustomAs<T> {
    type Output = (T, T);
    fn tuple_cast(self) -> <(S, S) as TupleCast<T>>::Output {
        (self.0.custom_as(), self.1.custom_as())
    }
}

// You would probably have more impls, up to some size limit.

// We can't use std::convert::From, because it isn't defined for the same
// basic types as the `as` operator is... which kinda sucks.  So, we have
// to implement the desired conversions ourselves.
//
// Since this would be hideously tedious, we can use a macro to speed things
// up a little.

trait CustomAs<T> {
    fn custom_as(self) -> T;
}

macro_rules! custom_as_impl {
    ($src:ty:) => {};
    ($src:ty: $dst:ty) => {
        impl CustomAs<$dst> for $src {
            fn custom_as(self) -> $dst {
                self as $dst
            }
        }
    };
    ($src:ty: $dst:ty, $($rest:ty),*) => {
        custom_as_impl! { $src: $dst }
        custom_as_impl! { $src: $($rest),* }
    };
}

// You could obviously list others, or do manual impls.
custom_as_impl! { u16: u16, u32, u64, i32, i64, f32, f64 }

fn main() {
    let x: (u16, u16) = (1, 2);
    let y: (f32, f32) = x.tuple_cast();
    println!("{:?}", y);
}

没有内置的方法来执行此操作,但可以使用宏来执行此操作:

macro_rules! tuple_as {
    ($t: expr, ($($ty: ident),*)) => {
        {
            let ($($ty,)*) = $t;
            ($($ty as $ty,)*) 
        }
    }
}

fn main() {
    let t: (u8, char, isize) = (97, 'a', -1);

    let other = tuple_as!(t, (char, i32, i8));

    println!("{:?}", other);
}

打印 ('a', 97, -1).

该宏仅适用于在名称为单个标识符的类型之间进行转换(这就是 : ident 所指的),因为它会重用这些名称来绑定到源元组的元素,以便能够投他们。所有基本类型都是有效的单一标识符,因此它适用于那些。

这个版本处理了更多的案例Playground Example

原始来源:

由于匹配规则,对于单一类型转换只需使用 as_tuple!(expr, T)as_tuple!(expr, (T))

其余部分与原始答案相同

macro_rules! tuple_as {
($t: expr, $ty: ident) => {{
    let (a, b) = $t;
    let a = a as $ty;
    let b = b as $ty;
    (a, b)
}};
($t: expr, ($ty: ident)) => {{
    let (a, b) = $t;
    let a = a as $ty;
    let b = b as $ty;
    (a, b)
}};
($t: expr, ($($ty: ident),*)) => {{
    let ($($ty,)*) = $t;
    ($($ty as $ty,)*)
}}}