[英]How can I return None when a given enum is not a certain variant?
I have the following enum defined: 我定义了以下枚举:
#[derive(Debug, Copy, Clone)]
struct Core;
#[derive(Debug, Copy, Clone)]
struct Mem;
#[derive(Debug, Copy, Clone)]
pub enum Atag {
Core(Core),
Mem(Mem),
Cmd(&'static str),
Unknown(u32),
None,
}
I would like to implement a function on this enum which "filters out" certain enum values. 我想在这个enum上实现一个“过滤掉”某些枚举值的函数。 I have the following:
我有以下内容:
impl Atag {
/// Returns `Some` if this is a `Core` ATAG. Otherwise returns `None`.
pub fn core(self) -> Option<Core> {
match self {
Atag::Core => Some(self),
_ => None
}
}
}
I'm not sure why, but the compiler complains: 我不知道为什么,但编译器抱怨:
error[E0532]: expected unit struct/variant or constant, found tuple variant `Atag::Core`
--> src/main.rs:17:13
|
17 | Atag::Core => Some(self),
| ^^^^^^^^^^ not a unit struct/variant or constant
help: possible better candidate is found in another module, you can import it into scope
|
1 | use Core;
|
I also tried a comparison approach: 我也尝试了一种比较方法:
pub fn core(self) -> Option<Core> {
if self == Atag::Core {
Some(self)
} else {
None
}
}
But the compiler complains: 但编译器抱怨:
error[E0369]: binary operation `==` cannot be applied to type `Atag`
--> src/main.rs:20:12
|
20 | if self == Atag::Core {
| ^^^^^^^^^^^^^^^^^^
|
= note: an implementation of `std::cmp::PartialEq` might be missing for `Atag`
I think this is just a limitation of the pattern matching and is designed to prevent unexpected behavior. 我认为这只是模式匹配的限制,旨在防止意外行为。
The full "definition" of an Atag
with type Core
is Atag::Core(raw::Core)
. 具有
Core
类型的Atag
的完整“定义”是Atag::Core(raw::Core)
。 Obviously, the contents of the Core
are irrelevant to you, but the compiler needs to know that everything is "accounted for" because the compiler is a stickler for the rules. 显然,
Core
的内容与您无关,但编译器需要知道所有内容都“被占用”,因为编译器是规则的坚持者。 The easiest way to get around this is to use the "anything pattern", _
, much like you did to match non- Core
variants. 解决这个问题的最简单方法是使用“任何模式”,
_
,就像你匹配非Core
变体一样。
impl Atag {
/// Returns `Some` if this is a `Core` ATAG. Otherwise returns `None`.
pub fn core(self) -> Option<Core> {
match self {
// The compiler now knows that a value is expected,
// but isn't necessary for the purposes of our program.
Atag::Core(_) => Some(self),
_ => None
}
}
}
To ignore multiple values, you'd use Something::Foo(_, _)
- one underscore for each value in the variant, or Something::Foo(..)
to ignore everything. 要忽略多个值,您可以使用
Something::Foo(_, _)
- 变量中的每个值使用一个下划线,或者忽略所有内容的Something::Foo(..)
。
Remember that, unlike in some other languages, a Rust enum is not "just" a collection of different types. 请记住,与其他语言不同,Rust枚举不仅仅是“不仅仅是”不同类型的集合。 Data associated with an enum value is a part of it, just like the fields of a structure.
与枚举值相关联的数据是其中的一部分,就像结构的字段一样。 So
self == Atag::Core
isn't a meaningful statement because it ignores the data associated with a Core
. 所以
self == Atag::Core
不是一个有意义的语句,因为它忽略了与Core
相关的数据。 A Foo(0)
is different than a Foo(12)
, even if they're both of the Foo
variant. Foo(0)
与Foo(12)
,即使它们都是Foo
变体。
I'd also like to point out if let
, which is - as far as I can tell - the closest option to a standard if
statement without defining a custom is_core
function on Atag
(which, given the existence of match
and if let
, is basically unnecessary). 我还想指出,
if let
- 就我所知 - 是最接近标准if
语句的选项而没有在Atag
上定义一个自定义的is_core
函数(假设存在match
, if let
,则是基本上没必要)。
impl Atag {
/// Returns `Some` if this is a `Core` ATAG. Otherwise returns `None`.
pub fn core(self) -> Option<Core> {
if let Atag::Core(_) = self {
Some(self)
} else {
None
}
}
}
I needed something like this to chain functions together nicely. 我需要这样的东西来很好地将函数链接在一起。 In that case, you want to return the unwrapped core type, rather than just the enum.
在这种情况下,您希望返回未包装的核心类型,而不仅仅是枚举。
I also found it easier to not consume the input, and so accepted a &self
argument an returned an Option<&Core>
. 我还发现不使用输入更容易,因此接受了一个
&self
参数并返回了一个Option<&Core>
。 But you can have both. 但你可以同时拥有两者。
The Rust convention has as_X
as the reference-based conversion and into_X
as the conversion that consumes the value. Rust约定将
as_X
作为基于引用的转换,将into_X
作为消耗该值的转换。 For example: 例如:
impl Atag {
fn as_core(&self) -> Option<&Core> {
if let Atag::Core(ref v) = self {
Some(v)
} else {
None
}
}
fn into_core(self) -> Option<Core> {
if let Atag::Core(v) = self {
Some(v)
} else {
None
}
}
}
fn main() {
let c = Atag::Core(Core {});
let m = Atag::Mem(Mem {});
assert_eq!(c.as_core().map(|cc| "CORE_REF"), Some("CORE_REF"));
assert_eq!(m.as_core().map(|cc| "CORE_REF"), None);
// Consume c - we cant use it after here...
assert_eq!(c.into_core().map(|cc| "NOM NOM CORE"), Some("NOM NOM CORE"));
// Consume m - we cant use it after here...
assert_eq!(m.into_core().map(|cc| "NOM NOM CORE"), None);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.