赞
踩
Rust 结构体用来进行自定义数据类型的定义。
Rust 有三种结构体类型:具名字段型结构体、元组型结构体、单元型结构体。
定义一个用户信息结构体。
fn main() { let user1 = User { active: true, username: String::from("frank"), email: String::from("example@xx.com"), sign_in_count: 1 }; println!("user1: {:?}", user1); // user1: User { active: true, username: "frank", email: "example@xx.com", sign_in_count: 1 } println!("user1 name: {}", user1.username); // user1 name: frank let username = "John".to_string(); let mut user2 = User { username, ..user1 }; user2.sign_in_count = 2; println!("user2: {:?}", user2); // user2: User { active: true, username: "John", email: "example@xx.com", sign_in_count: 2 } } #[derive(Debug)] #[allow(dead_code)] struct User { active: bool, username: String, email: String, sign_in_count: u64 }
Rust 中结构体类型都是需要驼峰式书写。字段和方法是小写,单词用下划线分割。
如果局部变量或参数与结构体字段同名,可进行简写。
访问结构体字段需要使用 .
运算符。
创建具名结构体值时,可以从另一个相同类型的结构体中为省略的字段提供值。需要使用 ..
表达式。
struct Bounds(usize, usize);
它保存的值称为元素,通过下标进行访问。
元组型结构体适用于创造新类型,即建立一个包含单组件的结构体,以获得更严格的类型检查。
没有元素的结构体类型。
struct Onesuch;
这种类型的值不占用内存,很像单元类型 ()
。Rust 既不会在内存中实际存储单元类型结构体的值,也不会生成代码来对它们进行操作,因为仅通过值的类型它就能知道关于值的所有信息。
使用 impl 为结构体定义方法。
pub struct Queue { older: Vec<char>, younger: Vec<char>, } impl Queue { pub fn push(&mut self, c: char) { self.younger.push(c); } pub fn pop(&mut self) -> Option<char> { if self.older.is_empty() { if self.younger.is_empty() { return None; } use std::mem::swap; swap(&mut self.older, &mut self.younger); self.older.reverse(); } self.older.pop() } }
impl
块中定义的函数称为 关联函数。也就是结构体方法。
Rust 会将调用关联函数的结构体值作为第一个参数传给方法,改参数具有特殊名称 self
,是 self: &Self
的简写。如果是可变引用写成 &mut self
,是 self: &mut Self
的简写。Self
指的结构体本身的类型。
方法的 self
参数也可以是 Box<Self>
类型、Rc<Self>
类型或 Arc<Self>
类型。这种方法只能在给定的指针类型上调用。调用该方法会将指针的所有权传给它。
但是通常不需要这么做。一个方法期望通过引用接受 self
,那它在任何指针类型上调用时都可以正常工作。
let mut bq = Box::new(Queue::new());
// Queue::push 需要一个 &mut self,但 bq 是一个 Box<Queue>
// 这没有问题,Rust 在调用期间从 Box 借入了 &mut Queue
bq.push('a');
对于方法调用和字段访问,Rust 会自动从 Box
、Rc
、Arc
等指针类型中借入引用,因此 &self
和 &mut self
几乎总是方法签名里的正确选择。
但是如果某些方法确实需要获取指向 Self
的指针的所有权,并且其调用者手头恰好有这样一个指针,那么 Rust 允许你将它作为方法的 self 参数传入。此时,必须写出 self
的类型。
impl Node {
fn append_to(self: Rc<Self>, parent: &mut Node) {
parent.children.push(self);
}
}
不接收 self
参数的方法,叫做类型关联函数。
例如定义一个构造函数,通常叫做 new
,或者 with_capacity
等。
impl Queue {
pub fn new() -> Queue {
Queue { older: Vec::new(), younger: Vec::new() }
}
}
与类型关联的值叫做关联常量。
pub struct Vector2 {
x: f32,
y: f32,
}
impl Vector2 {
const ZERO: Vector2 = Vector2 { x: 0.0, y: 0.0 };
const UNIT: Vector2 = Vector2 { x: 1.0, y: 0.0 };
const NAME: &'static str = "Vector2";
const ID: u32 = 18;
}
这些值是和类型本身相关联的,可以在不引用 Vector2 的任一实例的情况下使用它们。
let scaled = Vector2::UNIT.scaled_by(2.0);
pub struct Queue<T> { older: Vec<T>, younger: Vec<T> } impl<T> Queue<T> { pub fn new() -> Queue<T> { Queue { older: Vec::new(), younger: Vec::new() } } pub fn push(&mut self, t: T) { self.younger.push(t); } pub fn is_empty(&self) -> bool { self.older.is_empty() && self.younger.is_empty() } }
还能为特定类型实现方法:
impl Queue<f64> {
fn sum(&self) -> f64 {
// ...
}
}
Self
指的是类型 Queue<T>
,所以可以将 new
构造函数修改。
调用关联函数的时候可以使用 ::<>
比目鱼操作符来指定类型参数。通常 Rust 能推断出来。
let mut q = Queue::<char>::new();
struct Extrema<'elt> {
greatest: &'elt i32,
least: &'elt i32
}
可将 Extrema<'elt>
理解为:给定任意生命周期 'elt
,都可以创建一个 Extrema<'elt>
来持有对该生命周期的引用。
下面的函数会扫描切片并返回一个 Extrema
值,这个值的各个字段会引用其中的元素:
fn find_extrema<'s>(slice: &'s [i32]) -> Extrema<'s> {
let mut greatest = &slice[0];
let mut least = &slice[0];
for i in 1..slice.len() {
if slice[i] < *least { least = &slice[i]; }
if slice[i] > *greatest { greatest = &slice[i]; }
}
Extrema { greatest, least }
}
泛型结构体可接受常量值作为参数。定义一个表示任意次数多项式的类型:
/// N - 1 次多项式
struct Polynomial<const N: usize> {
/// 多项式系数
///
/// “对于多项式a + bx + cx^2 + ... + zx^(n-1),其第`i`个元素是xi的系数”
coefficients: [f64; N]
}
<const N:usize>
子句表示 Polynomial
类型需要一个 usize
值作为它的泛型参数,以此来决定要存储多少个系数。Polynomial<3>
是一个二次多项式。
也可在类型的关联函数中使用参数 N
。
impl<const N: usize> Polynomial<N> {
fn new(coefficients: [f64; N]) -> Polynomial<N> {
Polynomial { coefficients }
}
/// 计算 x 处的多项式的值
fn eval(&self, x: f64) -> f64 {
let mut sum = 0.0;
for i in (0..N).rev() {
sum = self.coefficients[i] + x * sum;
}
sum
}
}
如果结构体还接受其他种类的泛型参数,生命周期参数必须是第一,然后是类型,最后是 const
值。
定义一个结构体 Point
类型,但是它不能打印,不支持比较。此时需要使用 Rust 特性。这些特型 Rust 已经实现了,可以使用 #[derive]
属性添加到结构体。
#[derive(Copy, Clone, Debug, PartialEq)]
struct Point {
x: f64,
y: f64
}
需要一个不可变值中的一丁点可变数据,就是内部可变性。
Rust 提供了很多方案,两种直观的类型,即 Cell<T>
和 RefCell<T>
,都在 std::cell
模块中。
Cell<T>
是一个包含类型 T 的单个私有值的结构体。 Cell
唯一的特殊之处在于,即使对 Cell
本身没有 mut
访问权限,也可获取和设置这个私有值字段。
Cell::new(value) // 新建
cell.get() // 获取
cell.set(value) // 设置
Cell 不允许在共享值上调用 mut 方法。.get()
方法会返回 Cell 中值的副本,因此它仅在 T 实现了 Copy 特型时才有效。
如果需要一个引用,需要使用 RefCell<T>
,支持借用对其 T 值的引用。
RefCell::new(value) // 新建
ref_cell.borrow() // 借用
ref_cell.borrow_mut() // 可变借用
ref_cell.try_borrow() // 尝试借用
ref_cell.try_borrow_mut() // 尝试可变借用
教程代码仓库:https://github.com/zcfsmile/RustLearning/tree/main/struct_demo
参考链接:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。