循环结构
循环结构可以让您生成多个相同类型的元素。但为了得到高效的缓存,循环结构中必须提供键。
键是用来确定每一个数据对应的元素缓存的。如果不使用键,那么一个迭代器中如果元素发生中间插入、中间删除、调换顺序,则会导致一连串元素的属性需要重新设置,进而可能导致大量元素需要改变其缓存,造成性能损耗。
下面展示了如果没有键,插入一个新数据的过程。
elements 1 2 3 5 6 7
| | | | | |
data 1 2 3 5 6 7
^
|
插入4
所有数据对应的缓存将会后移一位,并在末尾创建一个新元素。
elements* 1 2 3 5 6 7 new
| | | | | | |
data 1 2 3 4 5 6 7
再更新缓存。显而易见,这样的操作改变了这4个元素的缓存。
|------------|
elements 1 2 3 | 4 5 6 7 |
| | | | | | | | |
data 1 2 3 | 4 5 6 7 |
|------------|
如果使用键,则第二步将变为:
在此插入新元素
|
v
elements* 1 2 3 5 6 7
| | | \ \ \
data 1 2 3 4 5 6 7
^
|
插入4
(new)
elements 1 2 3 4 5 6 7
| | | | | | |
data 1 2 3 4 5 6 7
这样,一次只需改变(初始化)一个元素的缓存就行了。
for表达式
for表达式中,@key
指令是可选的。如果不指定@key
,则默认使用迭代器元素作为键。这要求迭代器元素实现Clone + Hash + Eq + 'static
,若不满足,则引起编译期错误。此时,需要您通过@key
指令手动指定键。
#![allow(unused)] fn main() { build! { for num in 0..10 { Element1 { props: num } } } }
等同于
#![allow(unused)] fn main() { build! { for num in 0..10 { @key num; Element1 { props: num } } } }
下面这个例子将会引发编译期错误
#![allow(unused)] fn main() { let vec = vec![1, 2, 3]; build! { // 错误:num不满足`'static` for num in vec.iter() { Element1 { props: *num } } } }
解决办法是指定@key
,或改为iter().copied()
。
#![allow(unused)] fn main() { let vec = vec![1, 2, 3]; build! { for num in vec.iter() { @key *num; Element1 { props: *num } } } }
#![allow(unused)] fn main() { let vec = vec![1, 2, 3]; build! { for num in vec.iter().copied() { Element1 { props: *num } } } }
while表达式
与for表达式不同的是,while表达式必须指定键。
#![allow(unused)] fn main() { let mut iter = vec![1, 2, 3].into_iter(); build! { while let Some(num) = iter.next() { @key num; Element1 { props: num } } } }
如果您不知道指定什么键,请考虑下面的方案。这将回归到本章开始部分的“无键缓存方案”。
#![allow(unused)] fn main() { build! { for _ in (0..).take_while(|_| expression_here) { Element1 { props: 0 } } } }