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

参考链接:

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

相关推荐
GIS程序媛—椰子23 分钟前
【Vue 全家桶】7、Vue UI组件库(更新中)
前端·vue.js
DogEgg_00130 分钟前
前端八股文(一)HTML 持续更新中。。。
前端·html
ZL不懂前端32 分钟前
Content Security Policy (CSP)
前端·javascript·面试
木舟100936 分钟前
ffmpeg重复回听音频流,时长叠加问题
前端
王大锤43911 小时前
golang通用后台管理系统07(后台与若依前端对接)
开发语言·前端·golang
我血条子呢1 小时前
[Vue]防止路由重复跳转
前端·javascript·vue.js
黎金安1 小时前
前端第二次作业
前端·css·css3
啦啦右一1 小时前
前端 | MYTED单篇TED词汇学习功能优化
前端·学习
半开半落1 小时前
nuxt3安装pinia报错500[vite-node] [ERR_LOAD_URL]问题解决
前端·javascript·vue.js·nuxt