我最初的问题是将不同类型的元组转换为字符串.在Python中,这类似于:

>> a = ( 1.3, 1, 'c' )
>> b = map(  lambda x:  str(x), a )
['1.3', '1', 'c']

>> " ".join(b)
'1.3 1 c"

然而,Rust不支持元组上的映射——只支持类似向量的 struct .显然,这是因为能够将不同的类型打包到一个元组中,并且没有函数重载.此外,我也找不到在运行时获取元组长度的方法.所以,我想,需要一个宏来进行转换.

首先,我try 匹配元组的头,比如:

// doesn't work
match some_tuple {
    (a, ..) => println!("{}", a),
          _ => ()
}

我的问题是:

  1. 是否可以使用库函数将元组转换为字符串,并指定任意分隔符?
  2. 如何编写宏以将函数映射到任意大小的元组?

推荐答案

下面是一个非常聪明的宏解决方案:

trait JoinTuple {
    fn join_tuple(&self, sep: &str) -> String;
}

macro_rules! tuple_impls {
    () => {};

    ( ($idx:tt => $typ:ident), $( ($nidx:tt => $ntyp:ident), )* ) => {
        impl<$typ, $( $ntyp ),*> JoinTuple for ($typ, $( $ntyp ),*)
        where
            $typ: ::std::fmt::Display,
            $( $ntyp: ::std::fmt::Display ),*
        {
            fn join_tuple(&self, sep: &str) -> String {
                let parts: &[&::std::fmt::Display] = &[&self.$idx, $( &self.$nidx ),*];
                parts.iter().rev().map(|x| x.to_string()).collect::<Vec<_>>().join(sep)
            }
        }

        tuple_impls!($( ($nidx => $ntyp), )*);
    };
}

tuple_impls!(
    (9 => J),
    (8 => I),
    (7 => H),
    (6 => G),
    (5 => F),
    (4 => E),
    (3 => D),
    (2 => C),
    (1 => B),
    (0 => A),
);

fn main() {
    let a = (1.3, 1, 'c');

    let s = a.join_tuple(", ");
    println!("{}", s);
    assert_eq!("1.3, 1, c", s);
}

基本思想是我们可以把一个元组解压成一个&[&fmt::Display].一旦我们有了它,就可以直接将每个项目映射成一个字符串,然后用分隔符将它们组合起来.下面是它本身的样子:

fn main() {
    let tup = (1.3, 1, 'c');

    let slice: &[&::std::fmt::Display] = &[&tup.0, &tup.1, &tup.2];
    let parts: Vec<_> = slice.iter().map(|x| x.to_string()).collect();
    let joined = parts.join(", ");

    println!("{}", joined);
}

下一步是创建一个特征,并针对具体 case 实施:

trait TupleJoin {
    fn tuple_join(&self, sep: &str) -> String;
}

impl<A, B, C> TupleJoin for (A, B, C)
where
    A: ::std::fmt::Display,
    B: ::std::fmt::Display,
    C: ::std::fmt::Display,
{
    fn tuple_join(&self, sep: &str) -> String {
        let slice: &[&::std::fmt::Display] = &[&self.0, &self.1, &self.2];
        let parts: Vec<_> = slice.iter().map(|x| x.to_string()).collect();
        parts.join(sep)
    }
}

fn main() {
    let tup = (1.3, 1, 'c');

    println!("{}", tup.tuple_join(", "));
}

这只在特定大小的元组中实现了我们的特性,这在某些情况下可能是好的,但肯定还不是cool.standard library使用了一些宏来减少复制和粘贴的繁重工作,这是获得更多尺寸所需的.我决定even lazier岁,并减少该解决方案的复制和粘贴!

我没有清楚明确地列出元组的每个大小以及相应的索引/泛型名称,而是让宏递归.这样,我只需要列出一次,所有较小的大小只是递归调用的一部分.不幸的是,我不知道如何使它朝着一个前进的方向前进,所以我只是把所有的东西翻转过来,然后向后走.这意味着我们必须使用反向迭代器,这有一点效率低下,但总的来说,这应该是一个很小的代价.

Rust相关问答推荐

WebDriver等待三十四?(Rust Se)

关于Rust 中回归的逻辑

当第二个`let`依赖于第一个`let()`时,如何在一行中有多个`let()`?

为什么铁 rust S似乎有内在的易变性?

在我的Cargo 中,当我在建筑物中使用时,找不到我可以在产品包中使用的 crate .r我如何解决这个问题?

从未排序的链表中删除重复项的铁 rust 代码在ELSE分支的低级上做了什么?

完全匹配包含大小写的整数范围(&Q;)

对于rustc编译的RISC-V32IM二进制文件,llvm objdump没有输出

是否可以在不直接重复的情况下为许多特定类型实现一个函数?

RUST 中的读写器锁定模式

`tokio::pin` 如何改变变量的类型?

如何递归传递闭包作为参数?

打印 `format_args!` 时borrow 时临时值丢失

LinkedList::drain_filter::drop 中 DropGuard 的作用是什么?

为什么具有 Vec 变体的枚举没有内存开销?

具有生命周期和以后引用的可变方法

有没有办法阻止 rust-analyzer 使非活动代码变暗?

Rust 内联 asm 中的向量寄存器:不能将 `Simd` 类型的值用于内联汇编

在使用大型表达式时(8k 行需要一小时编译),是否可以避免 Rust 中的二次编译时间?

你能用 Rust 和 winapi 制作 Windows 桌面应用程序吗?