结构体(struct)是Rust中创建自定义类型的主要方式。它让你将相关的数据组合在一起,并通过方法(method)为其附加行为。本课将从基础到高级,全面掌握结构体的使用。
学习目标:掌握三种结构体语法、方法与关联函数、派生特征、结构体设计模式
struct User {
username: String,
email: String,
sign_in_count: u64,
active: bool,
}
fn main() {
// 创建实例(字段必须全部初始化)
let user1 = User {
username: String::from("alice"),
email: String::from("alice@example.com"),
sign_in_count: 1,
active: true,
};
// 访问字段
println!("用户名: {}", user1.username);
println!("邮箱: {}", user1.email);
// 可变结构体:整个实例可变,不能只标记部分字段
let mut user2 = User {
username: String::from("bob"),
email: String::from("bob@example.com"),
sign_in_count: 0,
active: true,
};
user2.sign_in_count += 1; // 修改字段
// 结构体更新语法(从其他实例继承字段)
let user3 = User {
username: String::from("charlie"),
email: String::from("charlie@example.com"),
..user1 // 其余字段从user1复制
};
// ⚠️ user1的username和email已被移动(String非Copy)
// println!("{}", user1.username); // ❌ 已移动
// 但sign_in_count和active是Copy类型,user1仍可用
println!("user1仍可用: active={}, count={}", user1.active, user1.sign_in_count);
}
// 有名字的元组
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!("红色: ({}, {}, {})", red.0, red.1, red.2);
println!("原点: ({}, {}, {})", origin.0, origin.1, origin.2);
// 类型不同,不能混用
// let c: Color = origin; // ❌ 类型不匹配
}
// 无字段,用于标记类型或实现trait
struct AlwaysEqual;
fn main() {
let _subject = AlwaysEqual;
println!("单元结构体大小: {} 字节", std::mem::size_of::());
}
✅ 验证通过
方法是在impl块中定义的函数,第一个参数是self,表示调用该方法的结构体实例。
#[derive(Debug)]
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(&self = &Rectangle)
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 into_components(self) -> (f64, f64) {
(self.width, self.height)
}
// 链式调用
fn set_width(&mut self, width: f64) -> &mut Self {
self.width = width;
self
}
fn set_height(&mut self, height: f64) -> &mut Self {
self.height = height;
self
}
}
fn main() {
let rect = Rectangle::new(10.0, 20.0);
println!("矩形: {:?}", rect);
println!("面积: {}", rect.area());
println!("周长: {}", rect.perimeter());
let square = Rectangle::square(5.0);
println!("正方形面积: {}", square.area());
println!("能容纳正方形? {}", rect.can_hold(&square));
// 链式调用
let mut r = Rectangle::new(1.0, 1.0);
r.set_width(30.0).set_height(40.0);
println!("链式设置后: {:?}", r);
// 获取所有权
let (w, h) = r.into_components();
println!("分解: width={}, height={}", w, h);
// r.area(); // ❌ r已被消耗
}
✅ 验证通过
&self方法:通过实例调用,如rect.area()Rectangle::new(),类似其他语言的静态方法
Rust可以通过#[derive]属性自动实现常用特征:
// Debug: 格式化输出 {:?} 和 {:#?}
// Clone: 允许深拷贝 .clone()
// Copy: 允许值复制(需要Clone)
// PartialEq, Eq: 允许 == 和 != 比较
// PartialOrd, Ord: 允许 > < >= <= 排序
// Hash: 允许作为HashMap的键
#[derive(Debug, Clone, PartialEq)]
struct Point {
x: f64,
y: f64,
}
impl Point {
fn new(x: f64, y: f64) -> Self { Point { x, y } }
fn distance_from_origin(&self) -> f64 {
(self.x * self.x + self.y * self.y).sqrt()
}
}
fn main() {
let p1 = Point::new(3.0, 4.0);
let p2 = p1.clone(); // Clone
println!("{:?}", p1); // Debug
println!("{:#?}", p1); // 漂亮Debug
println!("相等? {}", p1 == p2); // PartialEq
// 排序
let mut points = vec![
Point::new(3.0, 4.0), // 距离5
Point::new(1.0, 1.0), // 距离√2
Point::new(0.0, 5.0), // 距离5
];
points.sort_by(|a, b| {
a.distance_from_origin()
.partial_cmp(&b.distance_from_origin())
.unwrap()
});
for p in &points {
println!("距离: {:.2}", p.distance_from_origin());
}
}
✅ 验证通过
use std::fmt;
#[derive(Debug, Clone, PartialEq)]
enum BookStatus {
Available,
Borrowed { borrower: String, due_days: u32 },
Reserved(Vec<String>), // 等待列表
}
#[derive(Debug, Clone)]
struct Book {
isbn: String,
title: String,
author: String,
year: u32,
status: BookStatus,
}
impl Book {
fn new(isbn: &str, title: &str, author: &str, year: u32) -> Self {
Book {
isbn: isbn.to_string(),
title: title.to_string(),
author: author.to_string(),
year,
status: BookStatus::Available,
}
}
fn borrow(&mut self, borrower: &str, days: u32) -> Result<(), &str> {
match &self.status {
BookStatus::Available => {
self.status = BookStatus::Borrowed {
borrower: borrower.to_string(),
due_days: days,
};
Ok(())
}
BookStatus::Borrowed { .. } => Err("书已被借出"),
BookStatus::Reserved(_) => Err("书已被预约"),
}
}
fn return_book(&mut self) -> Result<(), &str> {
match &self.status {
BookStatus::Borrowed { .. } => {
self.status = BookStatus::Available;
Ok(())
}
_ => Err("书未被借出"),
}
}
fn is_available(&self) -> bool {
matches!(self.status, BookStatus::Available)
}
}
impl fmt::Display for Book {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "[{}] {} by {} ({})", self.isbn, self.title, self.author, self.year)
}
}
struct Library {
name: String,
books: Vec<Book>,
}
impl Library {
fn new(name: &str) -> Self {
Library { name: name.to_string(), books: Vec::new() }
}
fn add_book(&mut self, book: Book) {
self.books.push(book);
}
fn find_by_title(&self, keyword: &str) -> Vec<&Book> {
self.books.iter()
.filter(|b| b.title.to_lowercase().contains(&keyword.to_lowercase()))
.collect()
}
fn available_books(&self) -> Vec<&Book> {
self.books.iter().filter(|b| b.is_available()).collect()
}
fn borrow_book(&mut self, isbn: &str, borrower: &str, days: u32) -> Result<(), &str> {
let book = self.books.iter_mut()
.find(|b| b.isbn == isbn)
.ok_or("未找到此书")?;
book.borrow(borrower, days)
}
fn return_book(&mut self, isbn: &str) -> Result<(), &str> {
let book = self.books.iter_mut()
.find(|b| b.isbn == isbn)
.ok_or("未找到此书")?;
book.return_book()
}
fn stats(&self) -> (usize, usize) {
let total = self.books.len();
let available = self.available_books().len();
(total, available)
}
}
impl fmt::Display for Library {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "📚 {} ({}本藏书, {}本可借)",
self.name, self.books.len(), self.available_books().len())
}
}
fn main() {
let mut lib = Library::new("Rust图书馆");
lib.add_book(Book::new("978-1", "The Rust Programming Language", "Steve Klabnik", 2023));
lib.add_book(Book::new("978-2", "Rust in Action", "Tim McNamara", 2022));
lib.add_book(Book::new("978-3", "Programming Rust", "Jim Blandy", 2024));
println!("{}", lib);
// 借书
match lib.borrow_book("978-1", "Alice", 14) {
Ok(()) => println!("✅ Alice借书成功"),
Err(e) => println!("❌ {}", e),
}
// 重复借
match lib.borrow_book("978-1", "Bob", 7) {
Ok(()) => println!("✅ Bob借书成功"),
Err(e) => println!("❌ {}", e),
}
// 还书
lib.return_book("978-1").unwrap();
println!("📖 归还后: {}", lib);
// 搜索
let found = lib.find_by_title("rust");
println!("\n搜索'rust'的结果:");
for book in found {
println!(" {}", book);
}
}
✅ 验证通过
实现Vec2结构体,包含x和y字段,实现加法、减法、点积、长度等方法。
创建Celsius和Fahrenheit元组结构体,实现相互转换的方法。
实现泛型栈Stack<T>,支持push、pop、peek、is_empty、len方法。
🔒 下一课解锁:枚举与模式匹配 —— Rust中最优雅的控制流
use std::fmt;
// 新类型模式(Newtype)
struct Meters(f64);
struct Kilometers(f64);
impl Meters {
fn new(v: f64) -> Self { Meters(v) }
fn to_km(self) -> Kilometers { Kilometers(self.0 / 1000.0) }
}
impl Kilometers {
fn new(v: f64) -> Self { Kilometers(v) }
fn to_m(self) -> Meters { Meters(self.0 * 1000.0) }
}
impl fmt::Display for Meters {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}m", self.0) }
}
impl fmt::Display for Kilometers {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}km", self.0) }
}
// Builder模式
#[derive(Debug)]
struct HttpRequest {
url: String,
method: String,
headers: Vec<(String, String)>,
body: Option,
timeout: u64,
}
struct HttpRequestBuilder {
url: Option,
method: Option,
headers: Vec<(String, String)>,
body: Option,
timeout: u64,
}
impl HttpRequestBuilder {
fn new() -> Self {
HttpRequestBuilder {
url: None, method: None, headers: Vec::new(),
body: None, timeout: 30,
}
}
fn url(mut self, url: &str) -> Self { self.url = Some(url.to_string()); self }
fn method(mut self, method: &str) -> Self { self.method = Some(method.to_string()); self }
fn header(mut self, key: &str, value: &str) -> Self { self.headers.push((key.into(), value.into())); self }
fn body(mut self, body: &str) -> Self { self.body = Some(body.to_string()); self }
fn timeout(mut self, seconds: u64) -> Self { self.timeout = seconds; self }
fn build(self) -> Result {
Ok(HttpRequest {
url: self.url.ok_or("URL is required")?,
method: self.method.unwrap_or_else(|| "GET".to_string()),
headers: self.headers,
body: self.body,
timeout: self.timeout,
})
}
}
fn main() {
// 新类型
let distance = Meters::new(5000.0);
println!("{} = {}", distance, distance.to_km());
// Builder
let request = HttpRequestBuilder::new()
.url("https://api.example.com/data")
.method("POST")
.header("Content-Type", "application/json")
.header("Authorization", "Bearer token123")
.body(r#"{"key": "value"}"#)
.timeout(60)
.build()
.unwrap();
println!("请求: {:?}", request);
}
✅ 验证通过