我正在研究这个问题以回答我遇到的一个简单问题:我想要一个 if/else 块来决定迭代是否应该有一个附带的tqdm进度条:
fn run(n: usize, verbose: bool) {
let it = if verbose { tqdm(0..n) } else { 0..n };
for _ in it { _ }
}
上面的代码无法编译,因为 if/else 的两个分支具有不同的类型,一个是 a Range
,另一个是 a Tqdm<Range>
。但是,根据顶部链接的问题,我进行了以下修改,并且可以很好地编译:
fn run(n: usize, verbose: bool) {
let it: Box<dyn impl Iterator<Item = usize>> = if verbose {
Box::new(tqdm(0..n))
} else {
Box::new(0..n)
};
for _ in it { _ }
}
通过指定类型(这实际上是必要的,仅仅包装它Box
没有帮助),我可以使代码编译。但是,我天真地觉得这意味着对象有一个额外的堆分配,dyn Iterator
而之前所有东西(至少据我所知)或多或少都是在堆栈框架内处理的。我知道一般来说,动态调度(我认为这是但我不完全确定)确实会带来性能损失。
在包装之前Box
我确实尝试过只是注释dyn Iterator<Item = usize>
但是这不起作用,我猜是因为某种程度上Box
抽象是必要的,但我不太明白为什么。
有没有办法让这段代码或其背后的想法在不进行和堆分配的情况下进行编译?这是否没有我想象的那么重要?如果这是不可能的,为什么我不能像上面提到的那样在没有的情况下Box
做简单的事呢?dyn Iterator
Box
您说得对,这
Box<_>
会导致分配,并dyn
导致动态调度。一种非分配的替代方法
Box<dyn _>
是&dyn _
(像您的简单dyn
想法但具有正确的语法),但必须从仍然在范围内的位置(if
或else
块之外)借用:如果需要返回迭代器,上述方法将不起作用,因为您无法返回对局部变量的引用。相反,您可以使用以下构造,这也可以避免
dyn
: