Rust生命周期
终于讲到Rust最重要的机制之一了,生命周期机制
我们先复习一下垂悬引用
1 | { |
这一段代码是编译失败的,原因如下
我们可以看到有两个作用域
a和b,但是其实生命周期也是类似的概念,r的生命周期中a的范围里,x的生命周期中b的范围内
r保存的x的引用,当x销毁之后,r的引用也就失效了,因此也就产生了垂悬引用
这里有一个案例
1 | fn longer(s1: &str, s2: &str) -> &str { |
接收两个字符串引用,返回两个字符串引用中较长的一个
这段代码是不能通过编译的,因为返回值返回的引用可能会过期,例如这样调用
1 | fn main() { |
r最后接收的是s2的引用,但是等到我们使用r的时候,s2已经释放了,Rust是会消除一切可能导致危险的情况
生命周期注释
生命周期注释是描述引用生命周期的方法,相当于是给生命周期做一个标记
虽然这样并不能改变引用的生命周期,但是可以在合适的地方声明两个引用的生命周期一致
例如说
1 | &i32 // 常规引用 |
然后我们就可以用生命周期注释来改造这个函数
1 | fn longer<'a>(s1: &'a str, s2: &'a str) -> &'a str { |
调用的时候就可以正常调用了
1 | fn main() { |
结构体如何使用字符串
之前的样例中,我们使用字符串都是使用的String
类型,而不是str
类型,主要就是考虑到生命周期的问题
那如果我们需要使用str
,就需要设定生命周期
1 | struct Str<'a> { |
这里定义了一个Str结构体,包含一个str类型的字符串,生命周期和结构体生命周期相同
然后使用是这样的
1 | fn main() { |
这里都是没有问题的
那如果我们要给这个结构体赋予一个方法
1 | impl<'a> Str<'a> { |
这里的返回值类型没有写生命周期注释,加不加都可以的,现在已经可以自动判断生命周期了
静态生命周期
生命周期还有一个注释,'static
,所有使用双引号包括的字符串都是`&’static str
表示生命周期从程序的开始一直到程序的结束
Rust文件与IO
接收命令行参数
我们在学C/C++的时候是通过main函数接收参数的,一个字符串数组
但是Rust的main函数是一个午餐的函数,环境的参数是直接通过std::env
直接取出来的
例如
1 | fn main() { |
这个打印出来的第一个参数是当前运行的目录,而后续的参数可能是环境变量或者是命令行参数
命令行输入
我们可以直接使用std::io
这个模块来调用输入
1 | use std::io::stdin; |
read_line
可以直接读取一行信息到缓冲区,返回值是Result枚举,用于传递读取中的错误,可以用expect或者unwrap方法来处理错误
Rust中还没有提供直接从命令行读取数字或者格式化数据的方法,主要还是读取一行字符串,然后再用字符串识别函数处理数据
文件读取
这个就是用std::fs
模块即可
例如
1 | use std::fs; |
如果要读取二进制文件,则直接使用模块中的read函数即可
如果是需要读取大型文件,则可能需要流式处理的方式
例如说
1 | use std::io::prelude::*; |
File是描述文件的一个类,打开文件之后,就可以获取一个文件对象
然后可以通过对象的read方法读取字节到缓冲区,读取的字节数等于缓冲区的长度
文件写入
文件写入也分为一次写入和流式写入,流写入有打开方式,可以设置create和append
一次写入很简单
1 | use std::fs; |
流写入create需要调用对应的方法
1 | use std::io::prelude::*; |
写入的话就需要设置为mut
append就需要OpenOptions来设置
1 | use std::io::prelude::*; |