01. 结构体定义和使用
Rust 有三种结构体:普通结构体(命名字段)、元组结构体(像有名字的元组)、单元结构体(没有字段,像标记)。普通结构体最常用,字段用冒号赋值。更新结构体可以用结构体更新语法 ..,从另一个实例复制剩余字段。
rust
// 普通结构体
struct User {
name: String,
email: String,
age: u32,
active: bool,
}
let user1 = User {
name: String::from("小明"),
email: String::from("[email protected]"),
age: 18,
active: true,
};
// 结构体更新语法
let user2 = User {
name: String::from("小红"),
..user1 // 其余字段从 user1 复制
};
// 元组结构体
struct Point(i32, i32, i32);
let p = Point(0, 0, 0);
// 单元结构体
struct AlwaysEqual;
let subject = AlwaysEqual;结构体更新语法 ..user1 会转移 String 类型字段的所有权,user1 可能之后不能用。
02. 方法:impl 块
Rust 的方法在 impl 块里定义,第一个参数是 &self(不可变引用)、&mut self(可变引用)或 self(所有权转移)。关联函数(静态方法)没有 self 参数,用 Type::func() 调用,常用作构造函数。每个结构体可以有多个 impl 块,分开管理不同功能。
rust
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
// 关联函数(构造函数)
fn new(width: u32, height: u32) -> Self {
Self { width, height }
}
// 方法:计算面积
fn area(&self) -> u32 {
self.width * self.height
}
// 可变引用方法
fn scale(&mut self, factor: u32) {
self.width *= factor;
self.height *= factor;
}
// 判断能否包含另一个矩形
fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.height > other.height
}
}
let r = Rectangle::new(30, 50);
println!("面积: {}", r.area());Rust 没有继承,但 trait 和组合提供了类似的代码复用能力。
03. 泛型结构体
泛型让结构体可以存储不同类型的数据。在结构体名后面加 <T>,字段类型用 T。impl 块也要带泛型 <T>。多个泛型参数用逗号分隔,如 <T, U>。泛型不影响运行时性能,因为 Rust 会在编译时做单态化,为每个具体类型生成专用代码。
rust
struct Point<T> {
x: T,
y: T,
}
impl<T> Point<T> {
fn new(x: T, y: T) -> Self {
Self { x, y }
}
}
// 只对特定类型实现方法
impl Point<f64> {
fn distance_from_origin(&self) -> f64 {
(self.x.powi(2) + self.y.powi(2)).sqrt()
}
}
// 多个泛型参数
struct Pair<T, U> {
first: T,
second: U,
}
let p1 = Point { x: 5, y: 10 };
let p2 = Point { x: 1.5, y: 3.2 };
let pair = Pair { first: "hello", second: 42 };泛型代码写一遍,编译时自动为每个使用到的具体类型生成代码,零运行时开销。
04. 生命周期标注
生命周期是 Rust 独有的概念,当函数返回引用时,编译器需要知道这个引用能活多久。大部分时候编译器能自动推断(生命周期省略规则),推断不出来时才需要手动标注。生命周期语法是单引号加名字,如 'a,标注在引用类型前面:&'a str。标注不改变代码逻辑,只是帮助编译器做借用检查。
rust
// 需要生命周期标注的情况
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
// 'a 表示返回的引用和 x、y 中较短的那个活得一样久
fn main() {
let s1 = String::from("短");
let s2 = String::from("长一点");
let result = longest(&s1, &s2);
println!("较长的: {}", result);
// 结构体中的引用必须标注生命周期
struct Excerpt<'a> {
part: &'a str,
}
}生命周期省略规则覆盖了大部分常见情况,真的需要手写生命周期的时候远比想象中少。
05. 常用的派生宏
#[derive(...)] 是 Rust 最常用的属性,编译器自动帮结构体实现指定的 trait。#[derive(Debug)] 让结构体可以用 {:?} 打印调试输出。#[derive(Clone)] 实现深拷贝。#[derive(PartialEq)] 支持 == 比较。#[derive(Default)] 提供 Default::default()。大多数结构体第一行就是大长串的 derive。
rust
#[derive(Debug, Clone, PartialEq, Default)]
struct User {
name: String,
age: u32,
}
fn main() {
let u1 = User { name: "小明".into(), age: 18 };
let u2 = u1.clone();
// Debug 打印
println!("{:?}", u1); // 紧凑格式
println!("{:#?}", u1); // 美化格式
// PartialEq 支持比较
assert_eq!(u1, u2);
// Default
let u3 = User::default(); // name="", age=0
}几乎所有 struct 都至少会 derive Debug,调试时不能打印结构体太折磨了。
知识测验
第 1/4 题正确 0
Rust 中 impl 块的第一个参数 &self 表示什么?
下一节
下一节 集合类型