Jon*_*son 9 enums refactoring rust
我对 Rust 比较陌生。结果证明这个问题很长,所以我将从底线开始: 您更喜欢哪种解决方案?你有什么想法或意见吗?
我的代码无法编译,因为第 6 ( prev = curr) 行和第 12 ( bar(...)) 行使用了编译器怀疑可能未初始化的变量。作为程序员,我知道没有理由担心,因为第 6 行和第 12 行不会在第一次迭代期间运行。
let mut curr: Enum;
let mut prev: Enum;
for i in 0..10 {
if i > 0 {
prev = curr;
}
curr = foo();
if i > 0 {
bar(&curr, &prev);
}
}
Run Code Online (Sandbox Code Playgroud)
我知道您对编译器的了解是有限的。所以我想出了 3 种不同的方法来回答语言的安全限制。
1)初始化并停止想太多
我可以用任意值初始化变量。风险在于维护者可能会错误地认为那些最初的、希望未使用的值具有某些重要意义。第 1-2 行将变为:
let mut curr: Enum = Enum::RED; // Just an arbitrary value!
let mut prev: Enum = Enum::BLUE; // Just an arbitrary value!
Run Code Online (Sandbox Code Playgroud)
2)增加None价值Enum
第 1-2 行将变为
let mut curr: Enum = Enum::None;
let mut prev: Enum = Enum::None;
Run Code Online (Sandbox Code Playgroud)
我不喜欢这个解决方案的原因是现在我为我的枚举添加了一个新的可能和不自然的值。为了我自己的安全,我将不得不断言检查和匹配支行增加foo()和bar(),以及任何其他功能的使用Enum。
3) Option<Enum>
我认为这个解决方案是最“按书”的解决方案,但它使代码变得更长且更难理解。
第 1-2 行将变为
let mut curr: Option<Enum> = None;
let mut prev: Option<Enum> = None;
Run Code Online (Sandbox Code Playgroud)
这一次,None属于Option和不属于Enum。无辜的prev = curr声明会变成
prev = match curr {
Some(_) => curr,
None => panic!("Ah!")
};
Run Code Online (Sandbox Code Playgroud)
天真的调用bar()会丑化到
match prev {
Some(_) => bar(&curr, &prev),
None => panic!("Oy!")
};
Run Code Online (Sandbox Code Playgroud)
问题:您更喜欢哪种解决方案?你有什么想法或意见吗?
Pet*_*all 12
在可能的情况下,避免在 Rust 中使用过多的可变变量绑定,尤其是在循环中。使用变异变量管理循环内的状态会使代码更难理解并导致错误。在for循环中看到计数器变量通常是一个危险信号,通常可以将其替换为值的迭代器。
在您的情况下,您可以在 产生的值上创建一个迭代器foo,并将它们成对地传递给bar,使用tuple_windows 来自itertools板条箱:
use itertools::Itertools as _; // 0.9.0
use std::iter::repeat_with;
for (prev, curr) in repeat_with(foo).tuple_windows().take(9) {
bar(&curr, &prev);
}
Run Code Online (Sandbox Code Playgroud)
请注意,您不能在这里犯错而忘记设置prevor curr,并且未来的维护者也不能意外地交换两行并将其弄乱。
这种风格非常简洁和富有表现力。例如,我写上面的代码片段的方式强调bar将被调用 9 次。如果您更愿意强调foo将被调用 10 次,您也可以稍微修改它:
for (prev, curr) in repeat_with(foo).take(10).tuple_windows() {
bar(&curr, &prev);
}
Run Code Online (Sandbox Code Playgroud)
实际上,您表达的逻辑是所谓的“折叠”,您可以使用折叠而不是循环。
人们可以做的是:
(0..9).map(|_|foo()) //an iterator that outputs foo() 9 times, not 10
.fold(foo(), |prev, curr| {bar(&curr, &prev); curr});
Run Code Online (Sandbox Code Playgroud)
业务端当然是其中的一条线.fold。fold 的作用是它接受一个初始值的单个参数,对该初始值和迭代器产生的当前值调用一个二元函数,然后将结果用作下一次迭代的新初始值。我在这里简单地使用了一个函数,它被称为副作用,作为结果值使用该curr值,因此用作下一次迭代的初始值,并作为prev.
此表达式返回的最终值是 last curr,可以忽略它。
https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.fold