【问题标题】:How to define generic Iterator or Range variable in Rust? [duplicate]如何在 Rust 中定义泛型 Iterator 或 Range 变量? [复制]
【发布时间】:2022-01-05 06:53:15
【问题描述】:

有两个数字的输入数据,我需要构造一个迭代器或一个范围并进一步传递它:

// ordering is not strictly defined
let x1: usize = 4;
let x2: usize = 2;

let mut x_range = ...;

if x1 < x2 {
    x_range = (x1..=x2);
}
else {
    // i.e if we have ((x1, x2) == (3,1) it should produce 3,2,1 sequence.
    x_range = (x2..=x1).rev();
}

for x in x_range {
// do something
}

问题是正则范围表达式和rev()产生的两个范围变体具有不同的类型,并在代码中进一步抛出类型错误。

所以我尝试定义范围。


let mut x_range: std::ops::Range<usize>;

但是收到= note: expected struct std::ops::Range&lt;usize&gt; found struct RangeInclusive&lt;usize&gt;的错误:

然后我尝试定义一个迭代器,然后用into_iter() 转换范围。

let mut x_range: dyn Iterator<usize>;

但后来我收到了doesn't have a size known at compile-time

那么有没有办法将迭代器的抽象父接口或范围传递给其他函数?

【问题讨论】:

  • 这能回答你的问题吗? stackoverflow.com/questions/70238996/…
  • 是的,这应该可以解决问题!虽然我认为没有来自 Cormac 答案的外部板条箱的香草方式是可取的。

标签: rust


【解决方案1】:

来自Rust Reference

由于值属于哪个具体类型的不透明性,特征对象是动态大小的类型。像所有 DST 一样,特征对象在某种类型的指针后面使用;例如 &dyn SomeTrait 或 Box。每个指向 trait 对象的指针实例包括:

  • 指向实现 SomeTrait 的 T 类型实例的指针
  • 一个虚拟方法表,通常简称为 vtable,其中包含 T 实现的 SomeTrait 及其超特征的每个方法,一个指向 T 实现的指针(即函数指针)。

特征对象的目的是允许方法的“后期绑定”。在 trait 对象上调用方法会导致在运行时进行虚拟调度:也就是说,从 trait 对象 vtable 加载函数指针并间接调用。每个 vtable 条目的实际实现可能因对象而异。


dyn Iterator 的具体类型可以是任意大小,因此 Rust 不知道如何将它存储在堆栈中。您需要具有固定大小的间接级别(指针或引用)。在这种情况下,有几个可能的解决方案:

  1. 一个装箱的 trait 对象。 写成这样:

    let x_range: Box<dyn Iterator<Item = usize>> = if x1 < x2 {
        Box::new(x1..=x2)
    } else {
        Box::new((x2..=x1).rev())
    };
    

    Rust 会将具体类型移动到堆中并在堆栈上存储一个特征对象(请参阅the relevant chapter in the book)。这通常是最简单的选择。

  2. 具有自己的Iterator impl 的枚举。如果具体类型的集合是封闭的并且相当小(如您的情况),您可以编写一个包含任何可能的具体类型的类型:

    enum MyIterator {
       Forward(RangeInclusive<usize>),
       Reverse(Rev<RangeInclusive<usize>>),
    }
    
    impl Iterator for MyIterator {
        type Item = usize;
    
        fn next(&mut self) -> Option<Self::Item> {
            match self {
                MyIterator::Forward(ref mut it) => it.next(),
                MyIterator::Reverse(ref mut it) => it.next(),
            }
        }
    }
    
    let x_range = if x1 < x2 {
        MyIterator::Forward(x1..=x2)
    } else {
        MyIterator::Reverse((x2..=x1).rev())
    };
    

    这是一个稍微比装箱特征对象更有效的解决方案,因为它不需要堆分配并且调用Iterator::next 的间接级别少了一个。除非您在非常热的循环中调用 next(),否则效率提升可能微不足道。

【讨论】:

  • 语法编辑建议:我认为Box&lt;dyn Iterator&lt;Item = usize&gt;&gt; 有效,Box&lt;dyn Iterator&lt;Item = &lt;usize&gt;&gt;&gt; 版本无法编译。
猜你喜欢
  • 2021-12-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-03
  • 1970-01-01
  • 1970-01-01
  • 2011-12-10
  • 1970-01-01
相关资源
最近更新 更多