结构体是Rust中创建自定义类型的方式,允许你将多个相关的值组合在一起。
struct User { username: String, email: String, sign_in_count: u64, active: bool, }
let user1 = User { email: String::from("someone@example.com"), username: String::from("someusername123"), active: true, sign_in_count: 1, };
println!("用户名:{}", user1.username); println!("邮箱:{}", user1.email);
let mut user1 = User { email: String::from("someone@example.com"), username: String::from("someusername123"), active: true, sign_in_count: 1, }; user1.email = String::from("anotheremail@example.com");
注意: Rust不允许只将某个字段标记为可变,整个实例必须是可变的。
当变量名与字段名相同时,可以简写:
fn build_user(email: String, username: String) -> User { User { email, // 等价于 email: email username, // 等价于 username: username active: true, sign_in_count: 1, } }
let user2 = User { email: String::from("another@example.com"), username: String::from("anotherusername567"), ..user1 // 其余字段从user1获取 };
注意: 使用..语法会发生移动,如果user1中有被移动的值,user1将失效。
没有命名字段的结构体,称为元组结构体:
struct Color(i32, i32, i32); struct Point(i32, i32, i32); let black = Color(0, 0, 0); let origin = Point(0, 0, 0);
访问:
println!("R: {}, G: {}, B: {}", black.0, black.1, black.2);
用途:
没有任何字段的结构体:
struct AlwaysEqual; let subject = AlwaysEqual;
用途: 用于实现trait而不需要存储数据。
struct Rectangle { width: u32, height: u32, } impl Rectangle { fn area(&self) -> u32 { self.width * self.height } fn can_hold(&self, other: &Rectangle) -> bool { self.width > other.width && self.height > other.height } } fn main() { let rect1 = Rectangle { width: 30, height: 50 }; println!("面积是{}平方像素", rect1.area()); }
&self参数:
不带self参数的函数,通常用于构造函数:
impl Rectangle { fn square(size: u32) -> Rectangle { Rectangle { width: size, height: size, } } } // 使用::调用 let sq = Rectangle::square(3);
impl Rectangle { fn area(&self) -> u32 { self.width * self.height } } impl Rectangle { fn can_hold(&self, other: &Rectangle) -> bool { self.width > other.width && self.height > other.height } }
枚举允许你定义一个类型,它的值可以是几个可能的变体之一。
enum IpAddrKind { V4, V6, } let four = IpAddrKind::V4; let six = IpAddrKind::V6;
enum IpAddr { V4(u8, u8, u8, u8), V6(String), } let home = IpAddr::V4(127, 0, 0, 1); let loopback = IpAddr::V6(String::from("::1"));
enum Message { Quit, Move { x: i32, y: i32 }, Write(String), ChangeColor(i32, i32, i32), }
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::ChangeColor(r, g, b) => println!("Color: R{}, G{}, B{}", r, g, b), } } } let m = Message::Write(String::from("hello")); m.call();
Option是标准库中定义的最常用的枚举:
enum Option<T> { Some(T), None, }
使用Option:
let some_number = Some(5); let some_string = Some("a string"); let absent_number: Option<i32> = None;
Option与null的区别:
let x: i8 = 5; let y: Option<i8> = Some(5); // let sum = x + y; // 错误!不能相加
处理Option:
fn plus_one(x: Option<i32>) -> Option<i32> { match x { None => None, Some(i) => Some(i + 1), } } let five = Some(5); let six = plus_one(five); let none = plus_one(None);
常用方法:
let x: Option<i32> = Some(5); // unwrap(危险) let val = x.unwrap(); // 如果是None会panic // unwrap_or(安全) let val = x.unwrap_or(0); // None时返回默认值 // unwrap_or_else let val = x.unwrap_or_else(|| 0); // map let y = x.map(|v| v * 2); // Some(10) // is_some/is_none if x.is_some() { println!("有值"); } // if let if let Some(v) = x { println!("值是{}", v); }
Result用于可能失败的操作:
enum Result<T, E> { Ok(T), Err(E), }
使用Result:
use std::fs::File; let f = File::open("hello.txt"); let f = match f { Ok(file) => file, Err(error) => { panic!("打开文件失败:{:?}", error); } };
匹配不同错误:
use std::fs::File; use std::io::ErrorKind; let f = File::open("hello.txt"); let f = match f { Ok(file) => file, Err(error) => match error.kind() { ErrorKind::NotFound => match File::create("hello.txt") { Ok(fc) => fc, Err(e) => panic!("创建文件失败:{:?}", e), }, other_error => panic!("打开文件失败:{:?}", other_error), }, };
快捷方法:
// unwrap(危险) let f = File::open("hello.txt").unwrap(); // expect(带自定义消息) let f = File::open("hello.txt") .expect("hello.txt应该存在"); // unwrap_or_else let f = File::open("hello.txt").unwrap_or_else(|error| { if error.kind() == ErrorKind::NotFound { File::create("hello.txt").unwrap_or_else(|error| { panic!("创建文件失败:{:?}", error); }) } else { panic!("打开文件失败:{:?}", error); } });
// match写法 let some_value = Some(3); match some_value { Some(3) => println!("是三"), _ => (), } // if let写法 if let Some(3) = some_value { println!("是三"); }
带else:
if let Some(3) = some_value { println!("是三"); } else { println!("不是三"); }
let mut stack = Vec::new(); stack.push(1); stack.push(2); stack.push(3); // 弹出所有元素 while let Some(top) = stack.pop() { println!("{}", top); }
struct Rectangle { width: u32, height: u32, } impl Rectangle { fn area(&self) -> u32 { self.width * self.height } fn perimeter(&self) -> u32 { 2 * (self.width + self.height) } fn can_hold(&self, other: &Rectangle) -> bool { self.width >= other.width && self.height >= other.height } fn square(size: u32) -> Rectangle { Rectangle { width: size, height: size, } } } fn main() { let rect1 = Rectangle { width: 30, height: 50 }; let rect2 = Rectangle { width: 10, height: 40 }; let rect3 = Rectangle { width: 60, height: 45 }; println!("rect1的面积:{}", rect1.area()); println!("rect1的周长:{}", rect1.perimeter()); println!("rect1能容纳rect2:{}", rect1.can_hold(&rect2)); println!("rect1能容纳rect3:{}", rect1.can_hold(&rect3)); let sq = Rectangle::square(10); println!("正方形面积:{}", sq.area()); }
enum List { Cons(i32, Box<List>), Nil, } use List::{Cons, Nil}; fn main() { let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil)))))); // 遍历链表 fn print_list(list: &List) { match list { Cons(val, next) => { println!("{}", val); print_list(next); } Nil => println!("结束"), } } print_list(&list); }
#[derive(Debug)] enum Suit { Hearts, Diamonds, Clubs, Spades, } #[derive(Debug)] enum Rank { Ace, Number(u8), Jack, Queen, King, } #[derive(Debug)] struct Card { suit: Suit, rank: Rank, } impl Card { fn value(&self) -> u8 { match &self.rank { Rank::Ace => 11, Rank::Number(n) => *n, Rank::Jack | Rank::Queen | Rank::King => 10, } } } fn main() { let card = Card { suit: Suit::Hearts, rank: Rank::Ace, }; println!("{:?}", card); println!("牌面值:{}", card.value()); }
fn safe_divide(a: f64, b: f64) -> Option<f64> { if b == 0.0 { None } else { Some(a / b) } } fn main() { match safe_divide(10.0, 2.0) { Some(result) => println!("结果:{}", result), None => println!("除数不能为零"), } if let Some(result) = safe_divide(10.0, 0.0) { println!("结果:{}", result); } else { println!("除数不能为零"); } }
本章学习了Rust的结构体和枚举:
结构体和枚举是Rust类型系统的核心,掌握它们对于编写Rust程序至关重要。