假设我有下面的src/lib.rs
pub enum Toppings {
Almond,
Caramel,
Cookie,
Marshmallow,
ChocolateSprinkles,
}
pub enum Icecream {
Chocolate(Toppings),
Mint(Toppings),
Vanilla(Toppings),
}
我想允许模式解构s.t.人们可以像使用任何其他枚举一样使用库与关联数据:
use Icecream::*;
use Toppings::*;
fn order_how_many(icecream: Icecream) -> usize {
match icecream {
Chocolate(Marshmallow) => 42,
Vanilla(Cookie) => 69,
_ => 42069,
}
}
但同时我想禁止创建枚举及其相关数据的某些组合,例如,也许当地的冰淇淋店认为双巧克力太多,永远不会出售Icecream::Chocolate(Toppings::ChocolateSprinkles)
,所以我们应该完全禁止任何可能构建这种组合的可能性。我发现这在Rust中很难实现,因为我需要pub enum
来允许模式销毁,但是公开enum
意味着它的所有变体都是pub
。
我尝试了类似于有时可以在sealed trait pattern中找到的私有令牌,使用私有模块并通过#[forbid(missing_docs)]
,s.t.防止任何意外的pub use
,只有crate实现可以决定什么Icecream
/Toppings
组合是可能的,但这使得模式匹配丑陋(在每个模式破坏中需要_
或..
)。
mod icecream {
// Intentionally NOT use /// comment for this mod
// to prevent accidental `pub use`
#[forbid(missing_docs)]
mod hide {
pub struct Hide;
}
pub enum Toppings {
Almond,
Caramel,
Cookie,
Marshmallow,
ChocolateSprinkles,
}
pub enum Icecream {
Chocolate(Toppings, hide::Hide),
Mint(Toppings, hide::Hide),
Vanilla(Toppings, hide::Hide),
}
pub use Icecream::*;
pub use Toppings::*;
}
pub use icecream::*;
fn order_how_many(icecream: Icecream) -> usize { // OK
match icecream {
Chocolate(Marshmallow, _) => 42,
Vanilla(Cookie, ..) => 69,
_ => 42069,
}
}
fn str_to_icecream(base: &str, topping: &str) -> Option<Icecream> { // Compile fail as intended
if base.to_lowercase() == "chocollate" && topping.to_lowercase() == "chocolatesprinkles" {
Some(Chocolate(ChocolateSprinkles, icecream::hide::Hide))
} else {
None
}
}
在发布这个问题之前,SO建议我使用this,但它似乎也没有解决enum
的问题,因为enum
与struct
不同,它不能在类型本身及其关联成员之间具有不同的可见性,这将使具有不同形状的关联数据的实现更加麻烦,例如,如果元组的长度不同,我可能必须实现一个trait并返回trait对象,如果我使用这个方法的话。
有没有一种更优雅/自然/简单的方法来做到这一点?或者一开始就应该完全避免这样的代码,也许因为它被认为是一种糟糕的做法?
1条答案
按热度按时间t98cgbkg1#
如果不允许对
enum
进行构造,就不能允许对它们进行模式匹配。但是,
struct
字段可以是私有的,这样就可以执行以下操作:除了从同一个模块中的函数构造
Icecream
外,您不能再构造Icecream
;这些是唯一允许访问_secret
字段的内容。模块外部的代码仍然可以破坏这些值,但必须使用..
以避免命名_secret
字段。您可以将其他函数重写为: