跟AI学习Rust知识:常用类型和trait
最近在跟着Rust语言圣经学Rust,但是学完之后根本记不住那么多类型,想重新回去记下笔记又发现很多类型分散在各章实在不好找。然后突然想到现在AI都这么强了,何不向AI这个好老师学习一下呢。然后我就掏出我平时常用的DeesSeek,这个是我觉得最强的国产AI了(懒得折腾GPT4捏而且免费又够用)。下面就顺手记下AI老师的教学吧,以供后面回来查阅。
常用类型
我:可以详细说一下rust自带的一些基础封装类型有哪些吗,他们都是用来干什么的
DeepSeek-Coder-V2:
Rust 语言自带了许多基础封装类型,这些类型在标准库 std
中定义,用于处理各种常见的编程任务。以下是一些常见的 Rust 基础封装类型及其用途:
-
用途:表示一个值可能存在也可能不存在的情况。
Option<T>
有两个变体:Some(T)
表示值存在,None
表示值不存在。示例:
1
2let some_number = Some(5);
let no_number: Option<i32> = None;
-
用途:表示一个操作可能成功也可能失败的情况。
Result<T, E>
有两个变体:Ok(T)
表示操作成功并包含结果值,Err(E)
表示操作失败并包含错误信息。示例:
1
2
3
4
5
6
7fn divide(a: i32, b: i32) -> Result<i32, &'static str> {
if b == 0 {
Err("Cannot divide by zero")
} else {
Ok(a / b)
}
}
和Some一样同样是个枚举,不过多了个类型泛型,用于表示Err的类型
-
用途:动态数组,可以存储任意数量的元素。
Vec<T>
在堆上分配内存,并且可以动态增长和收缩。示例:
1
2
3let mut numbers = Vec::new();
numbers.push(1);
numbers.push(2);
还可以用
vec!
宏,代码let mut numbers = vec![1,2]
经过编译展开之后就会变成和上面代码一模一样
-
用途:用于处理可变的、可增长的 UTF-8 编码字符串。
String
是Vec<u8>
的封装,确保其内容是有效的 UTF-8 编码。示例:
1
2let mut s = String::from("Hello");
s.push_str(", world!");
-
用途:键值对存储结构,提供快速的查找、插入和删除操作。
HashMap<K, V>
使用哈希表实现,键和值的类型分别为K
和V
。示例:
1
2
3
4
5use std::collections::HashMap;
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);
-
用途:存储唯一值的集合,提供快速的查找、插入和删除操作。
HashSet<T>
使用哈希表实现,元素类型为T
。示例:
1
2
3
4
5use std::collections::HashSet;
let mut set = HashSet::new();
set.insert(1);
set.insert(2);
-
用途:在堆上分配值的智能指针。
Box<T>
用于在编译时无法确定大小的类型(如递归类型)或在堆上分配数据以减少栈上内存使用。示例:
1
let b = Box::new(5);
-
用途:引用计数智能指针,允许多个所有者共享数据。
Rc<T>
用于在堆上分配数据,并且允许多个变量引用同一个数据。示例:
1
2
3
4use std::rc::Rc;
let a = Rc::new(5);
let b = Rc::clone(&a);
-
用途:原子引用计数智能指针,允许多线程共享数据。
Arc<T>
类似于Rc<T>
,但可以安全地在多线程环境中使用。示例:
1
2
3
4
5
6
7
8
9use std::sync::Arc;
use std::thread;
let a = Arc::new(5);
let b = Arc::clone(&a);
thread::spawn(move || {
println!("{}", b);
});
这些基础封装类型在 Rust 编程中非常常用,它们提供了处理各种常见编程任务所需的功能和安全性。
我:可以再拓展说说其他的类型吗,比如Pin、Future,尽量多的说一下
DeepSeek-Coder-V2:
当然可以。除了之前提到的基础封装类型,Rust 还有一些更高级的类型和概念,这些在处理特定问题时非常有用。以下是一些额外的类型和概念:
-
用途:用于防止指针所指向的值被移动。
Pin<P>
确保指向的值不会被移动,这对于实现自引用结构和异步编程中的Future
非常重要。示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34use std::pin::Pin;
use std::marker::PhantomPinned;
struct Test {
a: String,
b: *const String,
_marker: PhantomPinned,
}
impl Test {
fn new(txt: &str) -> Self {
Test {
a: String::from(txt),
b: std::ptr::null(),
_marker: PhantomPinned,
}
}
fn init(self: Pin<&mut Self>) {
let self_ptr: *const String = &self.a;
let this = unsafe { self.get_unchecked_mut() };
this.b = self_ptr;
}
fn a(self: Pin<&Self>) -> &str {
&self.get_ref().a
}
fn b(self: Pin<&Self>) -> &String {
assert!(!self.b.is_null(), "Test::b called without Test::init being called first");
unsafe { &*(self.b) }
}
}
-
用途:表示一个异步计算的值。
Future
是一个 trait,定义了异步计算的生命周期和状态。示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
struct MyFuture {
count: u32,
}
impl Future for MyFuture {
type Output = u32;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
match self.count {
3 => Poll::Ready(3),
_ => {
self.count += 1;
cx.waker().wake_by_ref();
Poll::Pending
}
}
}
}
-
用途:用于多线程环境中的同步。
Mutex<T>
提供互斥锁,确保同一时间只有一个线程可以访问数据。RwLock<T>
提供读写锁,允许多个读取者或一个写入者访问数据。示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20use std::sync::{Arc, Mutex};
use std::thread;
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..10 {
let counter = Arc::clone(&counter);
let handle = thread::spawn(move || {
let mut num = counter.lock().unwrap();
*num += 1;
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!("Result: {}", *counter.lock().unwrap());
Channel
用途:用于线程间通信。Rust 标准库提供了多种通道类型,如
mpsc
(多生产者单消费者)、broadcast
(多生产者多消费者)等。示例:
1
2
3
4
5
6
7
8
9
10
11
12use std::sync::mpsc;
use std::thread;
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
let val = String::from("hi");
tx.send(val).unwrap();
});
let received = rx.recv().unwrap();
println!("Got: {}", received);
提一嘴常见的四种通道类型:mpsc,标准库,tokio, 多生产者,单消费者模式;oneshot, 单生产者,单消费者,一次只能发送一条消息(标准库没有,只有tokio有);broadcast,多生产者,多消费者,其中每一条发送的消息都可以被所有接收者收到,因此是广播(同样是只有tokio有);watch,单生产者,多消费者,只保存一条最新的消息,因此接收者只能看到最近的一条消息,例如,这种模式适用于配置文件变化的监听(同样是只有tokio有);还有一种特殊的:多生产者、多消费者,且每一条消息只能被其中一个消费者接收,如果有这种需求,可以使用 async-channel 包。
这些类型和概念在 Rust 中用于处理更复杂和高级的编程任务,如异步编程、并发和多线程通信。理解和掌握这些工具可以帮助你更有效地编写高效且安全的 Rust 代码。
我:还有别的常用类型上面没提到的吗
DeepSeek-Coder-V2:
当然,Rust 标准库还提供了许多其他常用类型和模块,以下是一些额外的常用类型和概念:
-
用途:用于内部可变性。
Cell<T>
提供了一种在不可变引用中修改值的方式,而RefCell<T>
提供了运行时的借用检查,允许在不可变上下文中进行可变访问。示例:
1
2
3
4use std::cell::RefCell;
let x = RefCell::new(vec![1, 2, 3]);
x.borrow_mut().push(4);
-
用途:写时克隆智能指针。
Cow<T>
代表“Clone-on-Write”,允许在需要时进行数据的克隆,适用于读多写少的场景。示例:
1
2
3
4
5
6
7
8
9
10use std::borrow::Cow;
fn abs_all(input: &mut Cow<[i32]>) {
for i in 0..input.len() {
let v = input[i];
if v < 0 {
input.to_mut()[i] = -v;
}
}
}
-
用途:表示时间间隔。
Duration
用于处理时间相关的计算,如延迟、超时等。示例:
1
2
3use std::time::Duration;
let five_seconds = Duration::from_secs(5);
-
用途:用于处理文件系统路径。
Path
是不可变的路径引用,而PathBuf
是可变的,类似于&str
和String
的关系。示例:
1
2
3
4use std::path::PathBuf;
let mut path = PathBuf::from("/usr/bin");
path.push("rustc");
-
用途:用于处理操作系统相关的字符串。
OsString
和OsStr
类似于String
和&str
,但它们不要求字符串是有效的 UTF-8 编码。示例:
1
2
3use std::ffi::OsString;
let os_string = OsString::from("example");
-
用途:双向链表。
LinkedList<T>
提供了双向链表的实现,允许在链表的任意位置进行高效的插入和删除操作。示例:
1
2
3
4
5use std::collections::LinkedList;
let mut list = LinkedList::new();
list.push_back(1);
list.push_back(2);
这些类型和模块在 Rust 编程中也非常常用,它们提供了处理各种特定任务所需的功能和灵活性。理解和掌握这些工具可以帮助你更有效地编写 Rust 代码。
感觉比较常用的就这些类型了。
常用trait
我:可以说说常用的一些trait以及用法吗
DeepSeek-Coder-V2:
在 Rust 中,trait 是定义共享行为的方式。它们类似于其他语言中的接口,但提供了更多的灵活性和功能。以下是一些常用的 trait 及其用法:
Debug
用途:用于格式化输出调试信息。通过实现
Debug
trait,可以在println!
等宏中使用{:?}
格式化字符串。示例:
1
2
3
4
5
6
7
8
struct Point {
x: i32,
y: i32,
}
let p = Point { x: 1, y: 2 };
println!("{:?}", p);
Display
用途:用于格式化输出用户可读的字符串。通过实现
Display
trait,可以在println!
等宏中使用{}
格式化字符串。示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15use std::fmt;
struct Point {
x: i32,
y: i32,
}
impl fmt::Display for Point {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}
let p = Point { x: 1, y: 2 };
println!("{}", p);
Clone
用途:用于创建类型的深拷贝。通过实现
Clone
trait,可以使用.clone()
方法进行克隆。示例:
1
2
3
4
5
6
7
8
struct Point {
x: i32,
y: i32,
}
let p1 = Point { x: 1, y: 2 };
let p2 = p1.clone();
Copy
用途:用于标记类型可以按位复制。通过实现
Copy
trait,可以在赋值和函数调用时自动进行按位复制。示例:
1
2
3
4
5
6
7
8
struct Point {
x: i32,
y: i32,
}
let p1 = Point { x: 1, y: 2 };
let p2 = p1;
Default
用途:用于提供类型的默认值。通过实现
Default
trait,可以使用.default()
方法获取默认值。示例:
1
2
3
4
5
6
7
struct Point {
x: i32,
y: i32,
}
let p = Point::default();
PartialEq
和Eq
用途:用于比较两个值是否相等。
PartialEq
提供部分相等性比较,而Eq
提供完全相等性比较。示例:
1
2
3
4
5
6
7
8
9
struct Point {
x: i32,
y: i32,
}
let p1 = Point { x: 1, y: 2 };
let p2 = Point { x: 1, y: 2 };
assert_eq!(p1, p2);
PartialOrd
和Ord
用途:用于比较两个值的大小。
PartialOrd
提供部分顺序比较,而Ord
提供完全顺序比较。示例:
1
2
3
4
5
6
7
8
9
struct Point {
x: i32,
y: i32,
}
let p1 = Point { x: 1, y: 2 };
let p2 = Point { x: 1, y: 3 };
assert!(p1 < p2);
Iterator
用途:用于定义迭代器。通过实现
Iterator
trait,可以创建自定义的迭代器。示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21struct Counter {
count: u32,
}
impl Iterator for Counter {
type Item = u32;
fn next(&mut self) -> Option<Self::Item> {
self.count += 1;
if self.count < 6 {
Some(self.count)
} else {
None
}
}
}
let mut counter = Counter { count: 0 };
for i in counter {
println!("{}", i);
}
From
和Into
用途:用于类型转换。
From
trait 提供了一种类型转换为另一种类型的方法,而Into
trait 是From
的逆操作。示例:
1
2
3
4
5
6
7
8
9
10
11
12struct Point {
x: i32,
y: i32,
}
impl From<(i32, i32)> for Point {
fn from((x, y): (i32, i32)) -> Self {
Point { x, y }
}
}
let p: Point = (1, 2).into();
AsRef
和AsMut
用途:用于类型之间的引用转换。
AsRef
提供不可变引用转换,而AsMut
提供可变引用转换。示例:
1
2
3
4
5
6
7
8
9
10
11
12
13struct Point {
x: i32,
y: i32,
}
impl AsRef<Point> for Point {
fn as_ref(&self) -> &Point {
self
}
}
let p = Point { x: 1, y: 2 };
let p_ref: &Point = p.as_ref();
这些 trait 在 Rust 编程中非常常用,它们提供了处理各种常见任务所需的功能和灵活性。理解和掌握这些 trait 可以帮助你更有效地编写 Rust 代码。
我:还有没有别的常用的trait没提到,比如像Deref等等,尽可能多的说一下
DeepSeek-Coder-V2:
当然,Rust 标准库中还有许多其他常用的 trait,以下是一些额外的常用 trait 及其用法:
Deref
和DerefMut
用途:用于实现智能指针。
Deref
trait 允许类型像引用一样工作,而DerefMut
允许类型像可变引用一样工作。示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14use std::ops::Deref;
struct MyBox<T>(T);
impl<T> Deref for MyBox<T> {
type Target = T;
fn deref(&self) -> &T {
&self.0
}
}
let x = MyBox(5);
assert_eq!(*x, 5);
Drop
用途:用于定义类型的析构行为。通过实现
Drop
trait,可以在变量离开作用域时执行自定义的清理代码。示例:
1
2
3
4
5
6
7
8
9
10
11struct CustomSmartPointer {
data: String,
}
impl Drop for CustomSmartPointer {
fn drop(&mut self) {
println!("Dropping CustomSmartPointer with data `{}`!", self.data);
}
}
let c = CustomSmartPointer { data: String::from("my stuff") };
Fn
、FnMut
和FnOnce
用途:用于定义闭包。
Fn
trait 表示不可变闭包,FnMut
表示可变闭包,FnOnce
表示只能调用一次的闭包。示例:
1
2
3
4
5
6
7
8
9fn call_with_one<F>(some_closure: F) -> i32
where
F: Fn(i32) -> i32,
{
some_closure(1)
}
let double = |x| x * 2;
assert_eq!(call_with_one(double), 2);
Add
、Sub
、Mul
、Div
等算术 trait用途:用于定义算术运算。这些 trait 允许自定义类型进行加、减、乘、除等算术操作。
示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22use std::ops::Add;
struct Point {
x: i32,
y: i32,
}
impl Add for Point {
type Output = Point;
fn add(self, other: Point) -> Point {
Point {
x: self.x + other.x,
y: self.y + other.y,
}
}
}
let p1 = Point { x: 1, y: 0 };
let p2 = Point { x: 2, y: 3 };
assert_eq!(p1 + p2, Point { x: 3, y: 3 });
Index
和IndexMut
用途:用于通过索引访问元素。
Index
trait 允许通过索引访问不可变元素,而IndexMut
允许通过索引访问可变元素。示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19use std::ops::Index;
struct Matrix {
data: Vec<Vec<i32>>,
}
impl Index<usize> for Matrix {
type Output = Vec<i32>;
fn index(&self, index: usize) -> &Vec<i32> {
&self.data[index]
}
}
let matrix = Matrix {
data: vec![vec![1, 2, 3], vec![4, 5, 6]],
};
assert_eq!(matrix[0][1], 2);
ToOwned
用途:用于从借用数据创建拥有数据。
ToOwned
trait 允许从借用数据创建拥有数据,类似于Clone
,但更通用。示例:
1
2
3
4use std::borrow::ToOwned;
let s: &str = "hello";
let owned: String = s.to_owned();
Borrow
和BorrowMut
用途:用于获取类型的借用。
Borrow
trait 允许获取不可变借用,而BorrowMut
允许获取可变借用。示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14use std::borrow::Borrow;
struct MyStruct {
data: String,
}
impl Borrow<str> for MyStruct {
fn borrow(&self) -> &str {
&self.data
}
}
let my_struct = MyStruct { data: String::from("hello") };
let s: &str = my_struct.borrow();
这些 trait 在 Rust 编程中也非常常用,它们提供了处理各种特定任务所需的功能和灵活性。理解和掌握这些 trait 可以帮助你更有效地编写 Rust 代码。
在学习编程方面,AI确实好使,有时候Rust的报错不知道怎么改,比如所有权、生命周期之类的问题,将代码、报错丢给AI改就能改好,当然并不是所有问题都能解决,但是对于我这种入门萌新还是能解决绝大部分问题的(小声)