如何遍历特征对象或未定型类型的元组

时间:2019-10-10 11:20:48

标签: rust iterator trait-objects mutable-reference

此问题最初是在this post in reddit中找到的。

尽管经验丰富的Rust用户会发现元组中的元素不必相同(如果是,则应该使用数组!),因此遍历它们并没有意义。在某些情况下,这很有用。

在这种情况下,元组的类型可以强制转换为相同的未调整大小的类型(例如[u8]dyn Trait)。

绝望:

trait Dummy {}
impl Dummy for () {}
impl Dummy for i32 {}

fn mut_tuple_to_iter(v: &mut ((), i32)) -> impl Iterator<Item = &mut dyn D> {
    //How do I implement this?
}

我找不到写上面的漂亮方法。有想法吗?


要查看一个可能不够美观的答案,这里是:

use core::iter::once;

trait D {}
impl D for () {}
impl D for i32 {}

fn mut_tuple_to_iter(v: &mut ((), i32)) -> impl Iterator<Item = &mut dyn D> {
    once(&mut v.0 as &mut dyn D).chain(once(&mut v.1 as &mut dyn D))
}

Play ground link

1 个答案:

答案 0 :(得分:1)

只需弄清楚我不必指定类型:

fn mut_tuple_to_iter(v:&mut ((), i32)) ->impl Iterator<Item=&mut dyn D> {
    once(&mut v.0 as _).chain(once(&mut v.1 as _))
}

将起作用。这使它已经不那么丑了!

当然,宏会有所帮助:

macro_rules! chained_elements {
    ($exp: expr) => {
        core::iter::once($exp as _)
    };
    ($exp: expr, $($rest:tt)*) => {
        core::iter::once($exp as _)
        .chain(chained_elements!($($rest)*))
    }
}

现在您可以写

use core::iter::once;

trait D {}
impl D for () {}
impl D for i32 {}

macro_rules! chained_elements {
    ($exp: expr) => {
        core::iter::once($exp as _)
    };
    ($exp: expr, $($rest:tt)*) => {
        core::iter::once($exp as _)
        .chain(chained_elements!($($rest)*))
    }
}

fn mut_tuple_to_iter(v: &mut ((), i32)) -> impl Iterator<Item = &mut dyn D> {
    chained_elements!(&mut v.0, &mut v.1)
}

Playground link

讨论

我一直在探索锈迹的设计空间,但是在某些情况下,以上是唯一的解决方案。

其原因是,当您必须使用特征对象(例如,以减少通用爆炸)时,由于按值调用不是一种选择(尚未实现),因此按可变引用进行调用是最佳选择你可以拥有的东西。在这种情况下,像上面这样的模式似乎是不可避免的。