简体   繁体   English

如何在 Rust 中定义通用迭代器或范围变量?

[英]How to define generic Iterator or Range variable in Rust?

Having input data of two numbers, I need to construct either an iterator or a range and pass it further:输入两个数字的数据后,我需要构造一个迭代器或一个范围并进一步传递它:

// 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
}

The problem is that two variants of ranges produced by regular range expression and rev() have different types and throw type errors further in the code.问题在于,由正则范围表达式和rev()生成的两个范围变体具有不同的类型,并在代码中进一步引发类型错误。

So I tried to define the range.所以我试图定义范围。


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

But received the error of = note: expected struct std::ops::Range<usize> found struct RangeInclusive<usize> :但收到= note: expected struct std::ops::Range<usize> found struct RangeInclusive<usize>的错误:

Then I tried to define an Iterator and then convert ranges with into_iter() .然后我尝试定义一个迭代器,然后用into_iter()转换范围。

let mut x_range: dyn Iterator<usize>;

But then I received doesn't have a size known at compile-time但是后来我收到doesn't have a size known at compile-time

So is there any way to pass an abstract parent interface of an iterator or a range to other functions?那么有没有办法将迭代器的抽象父接口或范围传递给其他函数?

From the Rust Reference :来自Rust 参考

Due to the opaqueness of which concrete type the value is of, trait objects are dynamically sized types.由于值属于哪个具体类型的不透明性,特征对象是动态大小的类型。 Like all DSTs, trait objects are used behind some type of pointer;像所有 DST 一样,特征对象在某种类型的指针后面使用; for example &dyn SomeTrait or Box.例如 &dyn SomeTrait 或 Box。 Each instance of a pointer to a trait object includes:每个指向特征 object 的指针实例包括:

  • a pointer to an instance of a type T that implements SomeTrait指向实现 SomeTrait 的类型 T 的实例的指针
  • a virtual method table, often just called a vtable, which contains, for each method of SomeTrait and its supertraits that T implements, a pointer to T's implementation (ie a function pointer).一个虚拟方法表,通常只称为 vtable,其中包含 T 实现的 SomeTrait 及其超特征的每个方法,指向 T 的实现的指针(即 function 指针)。

The purpose of trait objects is to permit "late binding" of methods.特征对象的目的是允许方法的“后期绑定”。 Calling a method on a trait object results in virtual dispatch at runtime: that is, a function pointer is loaded from the trait object vtable and invoked indirectly.在 trait object 上调用方法会导致在运行时进行虚拟调度:也就是说,从 trait object vtable 加载 function 指针并间接调用。 The actual implementation for each vtable entry can vary on an object-by-object basis.每个 vtable 条目的实际实现可能因对象而异。


The concrete type of dyn Iterator could be any size, so Rust doesn't know how to store it on the stack. dyn Iterator的具体类型可以是任意大小,因此 Rust 不知道如何将其存储在堆栈中。 You need a level of indirection (a pointer or reference) with a fixed size.您需要具有固定大小的间接级别(指针或引用)。 In this case, there are a couple probable solutions:在这种情况下,有几个可能的解决方案:

  1. A boxed trait object.盒装特征 object。 This is written like:这是这样写的:

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

    Rust will move the concrete type to the heap and store a trait object which on the stack (see the relevant chapter in the book ). Rust 会将具体类型移动到堆中,并在堆栈上存储一个 trait object(参见书中的相关章节)。 This is generally the simplest option.这通常是最简单的选择。

  2. An enum with its own Iterator impl.具有自己的Iterator impl 的枚举。 If the set of concrete types is closed and fairly small (as in your case), you can write a type that holds either possible concrete type:如果具体类型的集合是封闭的并且相当小(如您的情况),您可以编写一个包含任何可能的具体类型的类型:

     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()) };

    This is a very slightly more efficient solution than the boxed trait object, as it doesn't require a heap allocation and there's one fewer level of indirection to call Iterator::next .这是一种比盒装特征object更有效的解决方案,因为它不需要堆分配并且调用Iterator::next的间接级别少了一个。 That efficiency gain is probably insignificant unless you're calling next() in a very hot loop.除非您在非常热的循环中调用next() ,否则这种效率提升可能微不足道。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM