[Rust 入门] Rust 结构体总结

本教程环境

系统:MacOS

Rust 版本:1.77.2

Rust 结构体用来进行自定义数据类型的定义。 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 中结构体类型都是需要驼峰式书写。字段和方法是小写,单词用下划线分割。 如果局部变量或参数与结构体字段同名,可进行简写。 访问结构体字段需要使用 . 运算符。 创建具名结构体值时,可以从另一个相同类型的结构体中为省略的字段提供值。需要使用 .. 表达式。

元组型结构体

rust 复制代码
struct Bounds(usize, usize);

它保存的值称为元素,通过下标进行访问。 元组型结构体适用于创造新类型,即建立一个包含单组件的结构体,以获得更严格的类型检查。

单元型结构体

没有元素的结构体类型。

rust 复制代码
struct Onesuch;

这种类型的值不占用内存,很像单元类型 ()。Rust 既不会在内存中实际存储单元类型结构体的值,也不会生成代码来对它们进行操作,因为仅通过值的类型它就能知道关于值的所有信息。

用 impl 定义方法

使用 impl 为结构体定义方法。

rust 复制代码
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 指的结构体本身的类型。

以 Box、Rc 或 Arc 形式传入 self

方法的 self 参数也可以是 Box<Self> 类型、Rc<Self> 类型或 Arc<Self> 类型。这种方法只能在给定的指针类型上调用。调用该方法会将指针的所有权传给它。 但是通常不需要这么做。一个方法期望通过引用接受 self,那它在任何指针类型上调用时都可以正常工作。

rust 复制代码
let mut bq = Box::new(Queue::new());
// Queue::push 需要一个 &mut self,但 bq 是一个 Box<Queue>
// 这没有问题,Rust 在调用期间从 Box 借入了 &mut Queue
bq.push('a');

对于方法调用和字段访问,Rust 会自动从 BoxRcArc 等指针类型中借入引用,因此 &self&mut self 几乎总是方法签名里的正确选择。 但是如果某些方法确实需要获取指向 Self 的指针的所有权,并且其调用者手头恰好有这样一个指针,那么 Rust 允许你将它作为方法的 self 参数传入。此时,必须写出 self 的类型。

rust 复制代码
impl Node {
	fn append_to(self: Rc<Self>, parent: &mut Node) {
		parent.children.push(self);
	}
}

类型关联函数

不接收 self 参数的方法,叫做类型关联函数。 例如定义一个构造函数,通常叫做 new,或者 with_capacity 等。

rust 复制代码
impl Queue {
	pub fn new() -> Queue {
		Queue { older: Vec::new(), younger: Vec::new() }
	}
}

关联常量

与类型关联的值叫做关联常量。

rust 复制代码
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 的任一实例的情况下使用它们。

rust 复制代码
let scaled = Vector2::UNIT.scaled_by(2.0);

泛型结构体

rust 复制代码
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()
	}
}

还能为特定类型实现方法:

rust 复制代码
impl Queue<f64> {
	fn sum(&self) -> f64 {
		// ...
	}
}

Self 指的是类型 Queue<T>,所以可以将 new 构造函数修改。 调用关联函数的时候可以使用 ::<> 比目鱼操作符来指定类型参数。通常 Rust 能推断出来。

rust 复制代码
let mut q = Queue::<char>::new();

带生命周期参数结构体

rust 复制代码
struct Extrema<'elt> {
	greatest: &'elt i32,
	least: &'elt i32
}

可将 Extrema<'elt> 理解为:给定任意生命周期 'elt,都可以创建一个 Extrema<'elt> 来持有对该生命周期的引用。 下面的函数会扫描切片并返回一个 Extrema 值,这个值的各个字段会引用其中的元素:

rust 复制代码
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 }
}

带常量参数的泛型结构体

泛型结构体可接受常量值作为参数。定义一个表示任意次数多项式的类型:

rust 复制代码
/// 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

rust 复制代码
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 值。

让结构体类型实现某些公共特型(trait)

定义一个结构体 Point 类型,但是它不能打印,不支持比较。此时需要使用 Rust 特性。这些特型 Rust 已经实现了,可以使用 #[derive] 属性添加到结构体。

rust 复制代码
#[derive(Copy, Clone, Debug, PartialEq)]
struct Point {
	x: f64,
	y: f64
}

内部可变性

需要一个不可变值中的一丁点可变数据,就是内部可变性。 Rust 提供了很多方案,两种直观的类型,即 Cell<T>RefCell<T>,都在 std::cell 模块中。 Cell<T> 是一个包含类型 T 的单个私有值的结构体。 Cell 唯一的特殊之处在于,即使对 Cell 本身没有 mut 访问权限,也可获取和设置这个私有值字段。

rust 复制代码
Cell::new(value) // 新建
cell.get() // 获取
cell.set(value) // 设置

Cell 不允许在共享值上调用 mut 方法。.get() 方法会返回 Cell 中值的副本,因此它仅在 T 实现了 Copy 特型时才有效。 如果需要一个引用,需要使用 RefCell<T>,支持借用对其 T 值的引用。

rust 复制代码
RefCell::new(value) // 新建
ref_cell.borrow() // 借用
ref_cell.borrow_mut() // 可变借用
ref_cell.try_borrow() // 尝试借用
ref_cell.try_borrow_mut() // 尝试可变借用

教程代码仓库:github.com/zcfsmile/Ru...

参考链接:

🌟🌟 🙏🙏感谢您的阅读,如果对您有帮助,欢迎关注、点赞 🌟🌟

相关推荐
崔庆才丨静觅2 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60613 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了3 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅3 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅4 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅4 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment4 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅4 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊4 小时前
jwt介绍
前端
爱敲代码的小鱼5 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax