阶段性笔记(理解)
- 引用和借用
- 泛型
引用和借用
引用和借用都有生命周期。引用是作为参数传递给函数的地址。
创建一个变量,借用/引用都是指向同一个内存地址。只不过所有权不一样。
借用&: let b = &3;
引用ref: let ref c: i32 = 2;
这两种类型都是一样的,都是&i32
。
声明变量时,都可以用&或者ref,两者的效果是一样的。但是如果声明类型时,就只能用&了。
struct T<'a>{
// 这里只用&,如果用 ref 'a i32, 则会报错
v: &'a i32,
}
*号用来解引用,解引用之后才能对变量做计算操作。
泛型
泛型的目的是为了抽象化代码,理解为将类型做为参数传进去(公共的模板)。
但是这样的话就意味着可以传入所有的类型参数进去,可能这个函数接收到这个类型,函数里的代码有些方法对于这个类型来说用不了(比如说比较),就会编译失败。此时就需要对传入的类型参数做出一些限制。
对一个函数的泛型做出限制: fn findMax<T: PartialOrd + Copy>(list: &[T]) -> T{}
在枚举和结构体中
在结构体中声明了泛型后,里面的字段就必须使用这个泛型,不能声明了一次都不用。
生命周期
为了保证引用总是有效的,不产生悬垂指针。
这里就会涉及到了rust借用,借用是指一块内存空间的引用。可以理解为一个指针指向了这块内存地址。rust有一条借用规则:借用方的生命周期不能比出借方的生命周期还长。
意思是如果被借用方已经被释放了,此时借用的状态必须是不存在的。杜绝悬垂指针的出现。
生命周期在函数中的使用
如果有一个函数,它的参数和返回值都是引用,此时这个函数的参数是出借方,函数返回值所绑定到的那个变量就是借用方,这种函数也要满足上述的借用规则。也就是说,返回值引用绑定的变量的生命周期不能大于函数参数的生命周期。返回值引用绑定的变量 < 函数参数
fn max_num(x: &i32, y: &i32) -> &i32 {
if x > y {
&x
} else {
&y
}
}
fn main() {
let x = 1; // -------------+-- x start
let max; // -------------+-- max start
{ // |
let y = 8; // -------------+-- y start
max = max_num(&x, &y); // |
} // -------------+-- y over
println!("max: {}", max); // |
} // -------------+-- max, x over
为什么要手动添加生命周期标注?
如果不标记生命周期参数,那么编译器就不会知道这个函数返回的引用的生命周期是什么。
试想一下,如果在函数中,借用方的生命周期比出借方的高会发生什么
首先确定一点:这个函数的返回值是传入的参数。所以,假设如果参数已经被销毁了,但是这个函数的返回值却又指向了这个函数的参数,此时垂悬指针就发生了。
做了生命周期参数有什么用?
fn max_num<'a>(x: &'a i32, y: &'a i32) -> &'a i32 {
if x > y {
&x
} else {
&y
}
}
这里做了<'a>标记,作用是告诉编译器函数参数和函数返回值的生命周期一样长。
把x和y的生命周期和max_num的函数生命周期 'a 建立关联。但x和y的生命周期的长短是不一样的,其实是取两个参数中最短的生命周期作为关联。
函数的生命周期参数并不会改变生命周期的长短,只是用于编译来判断是否满足借用规则。
如果函数参数的生命周期参数与函数返回值的生命周期参数不建关联的话,那么生命周期参数就没有任何意义。
代码理解
也就是说,max的生命周期 = 函数返回的生命周期 = y的生命周期。即如果max离开了y所在的作用域后就会被销毁。
标识符的作用:fn max_num<'a>(x: &'a i32, y: &'a i32) -> &'a i32
- 第1个'a代表的意思是声明一个函数的生命周期参数。
- 第2个,第3个参数上的'a是代表使用这个生命周期标记(范围)'a。
- 第4个'a的意思是函数的返回值的声明周期也是'a,取参数间最短生命周期。
- 这个函数的返回值的生命周期也是'a
再复杂的一点代码
fn max_num<'a, 'b: 'a>(x: &'a i32, y: &'b i32) -> &'a i32 {
if x > y {
&x
} else {
&y
}
}
分析:
1.
这里的'a既是声明也是一个返回值。'b则