1.Rust中每一个引用都有生命周期,也就是引用保持有效的作用域。生命周期主要目标是避免悬垂引用,悬垂引用就是引用了已经释放的值。函数中,x的生命周期不能小于返回值得生命周期。当有x和y的时候,两者的生命周期是两个里面较小的那个。大多数情况下可以通过上下文判断出变量的生命周期,当借用检查器无法判断某些变量的生命周期时,就需要使用生命周期标注。
2.生命周期实例,总结网页生命周期例子,以及string和&str。结构体生命周期标注其实就是结构体里面的变量活的时间要比结构体实例长或者一样。标注的话也只是编译器不知道,需要标注,标注完它会检查。生命周期标注的省略:第一种就是如果只有一个输入生命周期参数,那么它将赋予所有输出生命周期参数。第二种就是如果方法有多个输入生命周期参数,不过其中之一因为方法的缘故为&self或者&mut self,那么self的生命周期被赋予所有输出生命周期参数。
3.move关键字会将闭包里使用的环境中的变量的所有权移进闭包。trait作为参数,就是说函数的参数item不管什么类型,需要实现trait的类型,函数调用传入的是一个类型。
4.每个迭代器都实现了Iterator trait,有next方法,这个trait定义在标准库中,next返回的是Option枚举类型,返回结果包裹在Some里面,当迭代器结束时,返回None。
使用v1.iter(),引用。迭代之后后面还能用,在不可变引用上创建迭代器,元素不可变。调用next方法时,需要使用可变引用mut类型。
使用v1.iter_mut(),引用。迭代可变的引用,元素是可变的,原来的vec!会改变数据,但是还是可用的。这里注意v的类型是引用类型,可以直接打印,但是计算的时候必须解引用。
使用into_iter(),移动。这种创建的迭代器会被移动到新的作用域上,会获得所有权,后面不能使用v1了。
消费型适配器,就是v1变成迭代器v1_iter之后,可以使用sum方法求和,这个时候v1_iter的所有权被转移到sum里面,就不可用了,但是v1还是可用的。
迭代适配器,就是把一个迭代器转换成其他类型的迭代器,使用collect只是变成一个集合。例子
let v2:Vec<_>=v1.iter().map(|x|x+1).collect()
let v2:Vec<_>=v1.iter().filter(|x|*x>5).collect()。
5.cargo工作空间:首先使用cargo.toml里面写几个成员,一个为main,另一些为lib,然后main调用lib函数,需要在main的依赖里导入lib的库,在代码中use进来。最后运行是使用-p参数来指定运行main函数。
智能指针和多线程部分
1.普通引用和智能指针的区别:引用只是借用数据的指针,而智能指针则是拥有它们指向的数据。智能指针通常使用结构体实现,区别于常规结构体的显著特征在于其实现了Deref和Drop trait。解引用就是用*来解引用,这里源码是固定代码实现&self.0,返回的也是&T。解引用多态,理解成自动解引用吧,就是明明是一个智能指针类型m,当函数参数传入时是&m,过程:将MyBox变为&String,再将String解引用,变为字符串slice,&str。一共有三种解引用多态与可变性交互。
2.Drop trait类似于其他语言中的析构函数了,当值离开作用域的时候执行此函数的代码。fn drop(&mut self),这个离开作用域是自动调用的,所以可以不用写出来。如果想提前释放,可以使用drop(b),这样不会造成二次释放。
3.智能指针一共有三种,一个是Box,解决编译时未知大小的类型,希望大量数据不被拷贝的情况下转移所有权。一个是Rc,这个是解决类似链表中的多个引用指向一个的情况,允许相同数据有多个所有者,使用Box会转移所有权,后面没法使用a了,就是链表连接问题。另一个是RefCell,由于Rc是不可不变引用,在需要修改时需要使用RefCell完成内部可变性(允许在使用不可变引用时改变数据)。
4.类似于Rc和RefCell只能用于单线程的场景。Rc允许相同数据有多个所有者,Box和RefCell有单一所有者。Box允许在编译时执行不可变或可变借用检查,Rc仅允许在编译时执行不可变借用检查,RefCell允许在运行时执行不可变或可变借用检查,所以可以在即便RefCell自身不可变的情况下修改其内部值。
5.Rc智能指针 let b=Cons(3,Rc::clone(&a),使用a.clone()也可以。使用Rc::strong_count(&a)来获取引用计数,释放之后就会减一。通过Rc允许程序的多个部分之间只读的共享数据,简单理解就是相同位置的多个可变引用会造成数据竞争。RefCell智能指针Cons(Rc<RefCell>,Rc),这里就是Rc里面套个RefCell,可以改变数据,改变方法是*value.borrow_mut()+=10。补充一个引用循环的例子,自己去看官方教程,比较不错。就是两个链表首位相连变成一个环,造成内存泄露。
6.为了解决引用循环的问题,有一个弱引用的概念Weak,弱引用通过Rc::down grade传递Rc实例的引用,调用Rc::downgrade会得到Weak类型的智能指针,同时将weak_count加一。区别在于weak_count无需计数为0就能使Rc实例被清理,只要strong_count为0就可以了。
7.有一个树形结构,自己看官方教程,就是如何使用rust来写一个树的数据结构。 多线程存在的问题:竞争状态,死锁,bug难以重现和修复。
8.使用.join()就会阻塞其他线程,等待此线程完成,返回为一个result枚举。线程外面定义的数据线程里面想要使用,这是一种安全的方式,比如里面使用了数据,但是外面在线程没运行完就drop数据,这是错误的。就需要使用move关键字,使用完之后后面的线程就无法使用,所有权被转移进使用的线程。
9.多线程通道介绍,rust实现消息传递并发的主要工具是通道,通道由两个部分组成,一个是发送端,一个是接收端,发送者或者接受者任一个被丢弃就可以认为通道被关闭了。
通过mpsc::channel创建通道,是多个生产者,单个消费者。
通过spmc::channel创建通道spmc是一个生产者,多个消费者。
tx.send(val).unwrap()
rx.recv().unwrap()
10.发送者的send方法返回的是一个result枚举,如果接收端已经被丢弃了,将没有发送的目标,此时发送会返回错误。接收者的recv返回值也是一个result类型,当通道发送端关闭时,返回一个错误值。接收端这里使用recv方法,会阻塞到有一个消息到来,我们也可以使用try_recv(),不会阻塞,会立即返回。值被移动到通道之后,会发生move操作,线程无法继续使用。rx自己实现了迭代器,可以直接遍历。实现多个生产者一个消费者的例子,就是把tx复制几份。
11.通道类似于单所有权的方式,值传递到通道之后,发送者就无法使用这个值了。共享内存类似于多所有权,即多个线程可以同时访问相同的内存位置。
互斥器:Mutex智能指针
1.任意时刻,只允许一个线程来访问某些数据。2.互斥器使用时,需要先获取到锁,使用后需要释放锁,这里释放超出作用域就会自动释放。
Mutex是一个智能指针,lock调用返回一个叫做MutexGuard的智能指针,内部提供了drop方法,实现当离开作用域时自动释放锁。
在创建多个线程时,我们使用move时会报错,因为move进去第一个线程时,后面就无法使用。这里考虑使用共享所有权的Rc智能指针,每次用都clone一下,之前讲过rc和refcell都是线程不安全的,无法使用。
引入Arc智能指针,每次用都clone一下,就是可以的了。Mutex类似于RefCell提供内部可变性。而RefCell和Rc是非线程安全的,Mutex和Arc是线程安全的。