[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...

参考链接:

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

相关推荐
Kika写代码44 分钟前
【微信小程序】4|搜索框-历史搜索 | 我的咖啡店-综合实训
前端·微信小程序·小程序·notepad++
egekm_sefg2 小时前
一个基于Rust适用于 Web、桌面、移动设备等的全栈应用程序框架
开发语言·前端·rust
冴羽2 小时前
Solid.js 最新官方文档翻译(13)—— Context(上下文)
前端·javascript·react.js
ObjectX前端实验室2 小时前
交互式md文档渲染实现
前端·github·markdown
励志成为大佬的小杨3 小时前
c语言中的枚举类型
java·c语言·前端
前端熊猫4 小时前
Element Plus 日期时间选择器大于当天时间置灰
前端·javascript·vue.js
傻小胖4 小时前
React 组件通信完整指南 以及 自定义事件发布订阅系统
前端·javascript·react.js
JaxNext4 小时前
开发 AI 应用的无敌配方,半小时手搓学英语利器
前端·javascript·aigc
万亿少女的梦1684 小时前
高校网络安全存在的问题与对策研究
java·开发语言·前端·网络·数据库·python
Python私教4 小时前
Vue3中的`ref`与`reactive`:定义、区别、适用场景及总结
前端·javascript·vue.js