结构体(struct)和枚举(enum)是Rust中组织数据的两种基本方式,配合模式匹配能写出表达力很强的代码。
struct定义
struct User {
name: String,
email: String,
age: u32,
active: bool,
}
fn main() {
let user = User {
name: String::from("Alice"),
email: String::from("alice@example.com"),
age: 28,
active: true,
};
println!("{} ({})", user.name, user.email);
}
字段初始化简写——变量名和字段名相同时可以省略:
fn new_user(name: String, email: String) -> User {
User {
name, // 等同于 name: name
email, // 等同于 email: email
age: 0,
active: true,
}
}
结构体更新语法:
let user2 = User {
email: String::from("bob@example.com"),
..user // 其余字段从user复制
};
impl:为结构体添加方法
struct Rectangle {
width: f64,
height: f64,
}
impl Rectangle {
// 关联函数(没有self参数),类似静态方法
fn new(width: f64, height: f64) -> Self {
Rectangle { width, height }
}
fn square(size: f64) -> Self {
Rectangle { width: size, height: size }
}
// 方法(有&self参数)
fn area(&self) -> f64 {
self.width * self.height
}
fn perimeter(&self) -> f64 {
2.0 * (self.width + self.height)
}
fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.height > other.height
}
// 可变借用
fn scale(&mut self, factor: f64) {
self.width *= factor;
self.height *= factor;
}
}
fn main() {
let mut rect = Rectangle::new(10.0, 5.0);
println!("Area: {}", rect.area()); // 50.0
println!("Perimeter: {}", rect.perimeter()); // 30.0
rect.scale(2.0);
println!("Scaled area: {}", rect.area()); // 200.0
let small = Rectangle::square(3.0);
println!("Can hold? {}", rect.can_hold(&small)); // true
}
元组结构体
没有字段名的结构体,适合简单的类型包装:
struct Color(u8, u8, u8);
struct Point(f64, f64, f64);
fn main() {
let red = Color(255, 0, 0);
let origin = Point(0.0, 0.0, 0.0);
println!("R={}, G={}, B={}", red.0, red.1, red.2);
}
常见用法是newtype模式——用元组结构体包装已有类型来获得类型安全:
struct Meters(f64);
struct Seconds(f64);
// Meters和Seconds是不同类型,不能混用
fn speed(distance: Meters, time: Seconds) -> f64 {
distance.0 / time.0
}
enum枚举
Rust的枚举比C/Java的枚举强大得多——每个变体可以携带数据:
enum IpAddr {
V4(u8, u8, u8, u8),
V6(String),
}
enum Message {
Quit, // 没有数据
Move { x: i32, y: i32 }, // 匿名结构体
Write(String), // 包含String
Color(u8, u8, u8), // 包含三个u8
}
impl Message {
fn call(&self) {
match self {
Message::Quit => println!("Quit"),
Message::Move { x, y } => println!("Move to ({}, {})", x, y),
Message::Write(text) => println!("Write: {}", text),
Message::Color(r, g, b) => println!("Color: #{:02x}{:02x}{:02x}", r, g, b),
}
}
}
fn main() {
let msgs = vec![
Message::Move { x: 10, y: 20 },
Message::Write(String::from("hello")),
Message::Color(255, 128, 0),
Message::Quit,
];
for msg in &msgs {
msg.call();
}
}
模式匹配 (match)
match必须穷尽所有可能:
enum Coin {
Penny,
Nickel,
Dime,
Quarter(UsState),
}
fn value_in_cents(coin: &Coin) -> u32 {
match coin {
Coin::Penny => {
println!("Lucky penny!");
1
}
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter(state) => {
println!("Quarter from {:?}", state);
25
}
}
}
_通配符匹配所有剩余情况:
fn describe_number(n: i32) -> &'static str {
match n {
0 => "zero",
1..=9 => "single digit",
10..=99 => "double digits",
_ => "large number",
}
}
Option和Result
Rust没有null,用Option<T>表示值可能不存在:
fn find_first_a(s: &str) -> Option<usize> {
s.find('a')
}
fn main() {
let text = "Rust language";
match find_first_a(text) {
Some(index) => println!("Found 'a' at index {}", index),
None => println!("No 'a' found"),
}
// if let:只关心一种情况时更简洁
if let Some(i) = find_first_a(text) {
println!("Index: {}", i);
}
// unwrap_or:提供默认值
let idx = find_first_a("xyz").unwrap_or(0);
// map:转换内部值
let doubled = Some(21).map(|x| x * 2); // Some(42)
}
Result<T, E>表示操作可能失败:
use std::fs;
use std::io;
use std::num::ParseIntError;
fn read_number_from_file(path: &str) -> Result<i32, String> {
let content = fs::read_to_string(path)
.map_err(|e| format!("Failed to read file: {}", e))?;
let number: i32 = content.trim().parse()
.map_err(|e: ParseIntError| format!("Failed to parse: {}", e))?;
Ok(number)
}
fn main() {
match read_number_from_file("number.txt") {
Ok(n) => println!("Number: {}", n),
Err(e) => eprintln!("Error: {}", e),
}
}
?操作符是错误传播的语法糖——如果Result是Err,提前返回错误;如果是Ok,解包值继续执行。
struct和enum是Rust类型系统的基石。struct用于组合相关数据,enum用于表达"多选一"的类型。掌握它们加上match模式匹配,就能表达大部分业务逻辑了。