在rust中,我有时需要编写链接的if-else语句。它们不是管理多个条件的最好方法,因为条件中有多个东西要检查。
下面是一个人工的rust-playgroung example,在下面的代码中,您可以看到所讨论的if-else链。
// see playground for rest of the code
fn check_thing(t: Thing) -> CarryRule {
let allowed = vec!["birds","dogs","cats","elefants","unknown","veggies","meat"];
let max_carry_size = 30;
let max_carry_unknown_size = 3;
let max_carry_dog_size = 5;
let typ = &t.typ.as_str();
if t.size > max_carry_size {
CarryRule::Forbidden
} else if ! allowed.contains(typ) {
CarryRule::Forbidden
} else if t.typ == "unknown" && t.size > max_carry_unknown_size {
CarryRule::Forbidden
} else if t.typ == "dogs" && t.size > max_carry_dog_size {
CarryRule::Forbidden
} else if t.typ == "birds" {
CarryRule::UseCage
} else {
CarryRule::UseBox
}
}
我知道,我应该使用match
语句,但我不知道如何使用单个匹配块来执行上述所有检查。
- 匹配
t.typ
- 检查
t.size
是否大于某个值 - 调用
allowed.contains(typ)
函数
我正在寻找Go语言的非参数化switch case的Rust版本,如下所示。
switch {
case a && b: return 1
case c || d: fallthrough
case e || f: return 2
default: return 0
}
当然,我也可以重构整个示例,以更一致的方式对t.size
、t.typ
和allowed
列表进行建模,以允许更好的match
块。但有时这些类型超出了我的控制范围,我不想用太多额外的 Package 来 Package 给定的类型。
在Rust中,有什么好的可读替代方案可以替代这种具有复杂条件的if-else链?
3条答案
按热度按时间qmelpv7a1#
如果你有一个
enum
和一个struct
,你可以更好地处理这个问题:现在您可以组成一个整洁的容器:
这里的想法是尝试使用一种更像Rust-like的表达式来表示您的情况,并使用
match
工具和元组来尽可能好地覆盖所有古怪的情况。尝试将其分解为一系列更小、更简单、更容易理解的测试也是一个好主意,比如
can_check()
的委托。pod7payv2#
您可以使用火柴防护装置:
不过,我认为使用
if
-else
链更好。woobm2wo3#
根据其他答案中的好建议,这里是一个playground和我的首选解决方案,它使用了以下新的
check_thing
函数。这个解决方案采用了所提出的枚举和匹配保护,但也关注可读性。它试图将所有的if-else逻辑保留在
check_thing
函数中,就像以前一样。和以前一样,它一步一步地应用这个逻辑,就像它在if-else链中一样。但让我们回到最初的问题:
在Rust中,有什么好的可读替代方案可以替代这种具有复杂条件的if-else链?
这里有一些可以帮助和补充使用的选项。
匹配和保护
使用一个简单的
match
语句(只有一个参数),并将一些复杂的语句实现为match guards。另外,不要使用字符串进行匹配,而是使用一个enum
。这样,rust编译器就可以帮助您覆盖所有的逻辑。一个带有一个参数和额外保护的较长的
match
可以保持相当的可读性,如果你正在寻找类似Go语言的普通switch
的东西,这是你能得到的最接近的东西。分开
将if-else块拆分为单独的部分,使用早期返回,而不是添加更多的else块(参见示例中的通用部分和特定部分)。如果匹配保护太复杂,请找到通用部分并将其上移。
泛化
将复杂的条件表达为对象的配置,而不是使用长的if-else链。使用
trait
或简单地添加enum
有助于捕获一些逻辑,并使实体更具可配置性。如果你从一个复杂的if-else链开始,这种重构和泛化需要更多的代码修改,但是它可以使你的逻辑变得可配置和可扩展,如果你是代码库的所有者,它可能是首选的解决方案。
这是一个广义的版本。
正如您所看到的,它将
Typ
和size
逻辑分开,这样做的好处是使内容可配置和可重用,但缺点是进位逻辑现在在不同的地方,您不能再像在最初的if-else块或在本文开头我的首选解决方案中那样从上到下阅读规则。你应该采取哪种解决方案取决于你的项目要求。
最后,感谢您的所有意见和答案,并教我一些更多的 rust 今天!