0
点赞
收藏
分享

微信扫一扫

rust 枚举值

可以像这样创建 IpAddrKind 两个不同成员的实例:

# V4,
# V6,
# }
#
# fn main() {
let four = IpAddrKind::V4;
let six = IpAddrKind::V6;
#
# route(IpAddrKind::V4);
# route(IpAddrKind::V6);
# }
#
# fn route(ip_kind: IpAddrKind) {}

注意枚举的成员位于其标识符的命名空间中,并使用两个冒号分开。这么设计的益处是现在 IpAddrKind::V4和 IpAddrKind::V6 都是 IpAddrKind 类型的。例如,接着可以定义一个函数来获取任何 IpAddrKind:

# V4,
# V6,
# }
#
# fn main() {
# let four = IpAddrKind::V4;
# let six = IpAddrKind::V6;
#
# route(IpAddrKind::V4);
# route(IpAddrKind::V6);
# }
#
fn route(ip_kind: IpAddrKind) {}

现在可以使用任一成员来调用这个函数:

# V4,
# V6,
# }
#
# fn main() {
# let four = IpAddrKind::V4;
# let six = IpAddrKind::V6;
#
route(IpAddrKind::V4);
route(IpAddrKind::V6);
# }
#
# fn route(ip_kind: IpAddrKind) {}

使用枚举甚至还有更多优势。进一步考虑一下我们的 IP 地址类型,目前没有一个存储实际 IP 地址 数据的方法;只知道它是什么 类型的。考虑到已经在第五章学习过结构体了,你可能会像示例 6-1 那样处理这个问题:

enum IpAddrKind {
V4,
V6,
}
struct IpAddr {
kind: IpAddrKind,
address: String,
}
let home = IpAddr {
kind: IpAddrKind::V4,
address: String::from("127.0.0.1"),
};
let loopback = IpAddr {
kind: IpAddrKind::V6,
address: String::from("::1"),
};
# }

这里我们定义了一个有两个字段的结构体 IpAddr:IpAddrKind(之前定义的枚举)类型的 kind 字段和 String 类型 address 字段。我们有这个结构体的两个实例。第一个,home,它的 kind 的值是IpAddrKind::V4 与之相关联的地址数据是 127.0.0.1。第二个实例,loopback,kind 的值是 IpAddrKind 的另一个成员,V6,关联的地址是 ::1。我们使用了一个结构体来将 kind 和 address 打包在一起,现在枚举成员就与值相关联了。 我们可以使用一种更简洁的方式来表达相同的概念,仅仅使用枚举并将数据直接放进每一个枚举成员而不是将枚举作为结构体的一部分。IpAddr 枚举的新定义表明了 V4 和 V6 成员都关联了 String 值:

enum IpAddr {
V4(String),
V6(String),
}
let home = IpAddr::V4(String::from("127.0.0.1"));
let loopback = IpAddr::V6(String::from("::1"));
# }

我们直接将数据附加到枚举的每个成员上,这样就不需要一个额外的结构体了。这里也很容易看出枚举工作的另一个细节:每一个我们定义的枚举成员的名字也变成了一个构建枚举的实例的函数。也就是说,IpAddr::V4() 是一个获取 String 参数并返回 IpAddr 类型实例的函数调用。作为定义枚举的结果,这些构造函数会自动被定义。 用枚举替代结构体还有另一个优势:每个成员可以处理不同类型和数量的数据。IPv4 版本的 IP 地址总是含有四个值在 0 和 255 之间的数字部分。如果我们想要将 V4 地址存储为四个 u8 值而 V6 地址仍然表现为一个 String,这就不能使用结构体了。枚举则可以轻易的处理这个情况:

enum IpAddr {
V4(u8, u8, u8, u8),
V6(String),
}
let home = IpAddr::V4(127, 0, 0, 1);
let loopback = IpAddr::V6(String::from("::1"));
# }

这些代码展示了使用枚举来存储两种不同 IP 地址的几种可能的选择。然而,事实证明存储和编码 IP 地址实在是太常见了以致标准库提供了一个开箱即用的定义!让我们看看标准库是如何定义 IpAddr 的:它正有着跟我们定义和使用的一样的枚举和成员,不过它将成员中的地址数据嵌入到了两个不同形式的结构体中,它们对不同的成员的定义是不同的:

// --snip--
}
struct Ipv6Addr {
// --snip--
}
enum IpAddr {
V4(Ipv4Addr),
V6(Ipv6Addr),
}

这些代码展示了可以将任意类型的数据放入枚举成员中:例如字符串、数字类型或者结构体。甚至可以包含另一个枚举!另外,标准库中的类型通常并不比你设想出来的要复杂多少。 注意虽然标准库中包含一个 IpAddr 的定义,仍然可以创建和使用我们自己的定义而不会有冲突,因为我们并没有将标准库中的定义引入作用域。第七章会讲到如何导入类型。

Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
#
# fn main() {}

这个枚举有四个含有不同类型的成员: • Quit 没有关联任何数据。 • Move 类似结构体包含命名字段。 • Write 包含单独一个 String。 • ChangeColor 包含三个 i32。 如下这些结构体 可以包含与之前枚举成员中相同的数据:

struct MoveMessage {
x: i32,
y: i32,
}
struct WriteMessage(String); // 元组结构体
struct ChangeColorMessage(i32, i32, i32); // 元组结构体
#
# fn main() {}

Message 枚举那样,轻易的定义一个能够处理这些不同类型的结构体的函数,因为枚举是单独一个类型。 结构体和枚举还有另一个相似点:就像可以使用 impl 来为结构体定义方法那样,也可以在枚举上定义方法。这是一个定义于我们 Message 枚举上的叫做 call 的方法:

# enum Message {
# Quit,
# Move { x: i32, y: i32 },
# Write(String),
# ChangeColor(i32, i32, i32),
# }
#
impl Message {
fn call(&self) {
// 在这里定义方法体
}
}
let m = Message::Write(String::from("hello"));
m.call();
# }

方法体使用了 self 来获取调用方法的值。这个例子中,创建了一个值为 Message::Write(String::from(”hello”)) 的变量 m,而且这就是当 m.call () 运行时 call 方法中的 self 的值。让我们看看标准库中的另一个非常常见且实用的枚举:Option。

举报

相关推荐

0 条评论