CRust学习笔记:生命周期-1

本系列文章是jon gjengset发布的crust of rust系列视频的学习笔记,crust of rust是一系列持续更新的rust中级教程。
在这篇文章中,我们将研究一个需要显式注释多个生命期的例子。我们还将讨论不同字符串类型之间的一些差异,以及在自定义的trait上引入泛型。这个例子是根据给定的字符串和分隔符对字符串进行拆分。
创建一个新项目:
cargo new --lib strsplit  lib.rs中写入如下代码: 1#[derive(debug)] 2pub struct strsplit { 3    remainder: &'a str,  4    delimiter: &'a str, 5} 6 7#[allow(dead_code)] 8impl strsplit { 9    pub fn new(haystack: &'a str, delimiter: &'a str) -> self {10        self {11            remainder: haystack,12            delimiter,13        }14    }15}1617impl iterator for strsplit {18    type item = &'a str;1920    fn next(&mut self) -> option {21        if let some(next_delim) = self.remainder.find(self.delimiter) {22            let until_remainder = &self.remainder[..next_delim];23            self.remainder = &self.remainder[next_delim + self.delimiter.len()..];24            some(until_remainder)25        }else if self.remainder.is_empty() {26            none27        }else {28            let rest = self.remainder;29            // 为什么空字符串可以赋值给self.remainder ?30            self.remainder = ;31            some(rest)32        }33    }34}3536#[test]37fn it_works() {38    let haystack = a b c d e;39    let letters: vec = strsplit::new(haystack,  ).collect();40    assert_eq!(letters, vec![a, b, c, d, e]);41}   
strsplit 与成员 remainder,delimiter 拥有相同的生命周期
使用fn new()方法构建的strsplit与传入的参数haystack,delimiter 拥有相同的生命周期
在实现iterator trait时,迭代的结果也要与strsplit拥有相同的生命周期,是因为要在strsplit的成员remainder上做迭代。
  在第30行,为什么空字符串可以赋值给self.remainder?这是因为self.remainder的生命周期是&'a str,空字符串的生命周期是&'static str,static的生命周期一直到程序结束。     修复bug 这里有一个bug,添加如下测试方法:
1#[test]2fn tail() {3    let haystack = a b c d ;4    let letters: vec = strsplit::new(haystack,  ).collect();5    assert_eq!(letters, vec![a, b, c, d, ]);6}  执行cargo test:running 2 teststest str_split_1::it_works ... oktest str_split_1::tail ... failed    我们将struct strsplit的成员remainder定义为option类型来修复这个bug: 1/** 2 * strsplit 与成员 remainder,delimiter 拥有相同的生命周期 3 */ 4#[derive(debug)] 5pub struct strsplit { 6    // 使用option 7    remainder: option,  8    delimiter: &'a str, 9}1011#[allow(dead_code)]12impl strsplit {13    /**14     * 新构建的strsplit与传入的参数haystack,delimiter 拥有相同的生命周期15     */16    pub fn new(haystack: &'a str, delimiter: &'a str) -> self {17        self {18            remainder: some(haystack),19            delimiter,20        }21    }22}2324impl iterator for strsplit {25    // 迭代的结果也要与strsplit拥有相同的生命周期,是因为要在strsplit的成员remainder上做迭代。26    type item = &'a str;2728    fn next(&mut self) -> option {29        // 这里为什么用some(ref mut remainder),而不用some(&mut refmainder) ?30        if let some(ref mut remainder) = self.remainder {31            if let some(next_delim) = remainder.find(self.delimiter) {32                let until_remainder = &remainder[..next_delim];33                *remainder = &remainder[next_delim + self.delimiter.len()..];34                some(until_remainder)35            }else {36                self.remainder.take()37            }38        }else {39            none40        }41    }42}4344#[test]45fn it_works() {46    let haystack = a b c d e;47    let letters: vec = strsplit::new(haystack,  ).collect();48    assert_eq!(letters, vec![a, b, c, d, e]);49}5051#[test]52fn tail() {53    let haystack = a b c d ;54    let letters: vec = strsplit::new(haystack,  ).collect();55    assert_eq!(letters, vec![a, b, c, d, ]);56}  执行cargo test,测试通过:running 2 teststest str_split_2::tail ... oktest str_split_2::it_works ... ok      在上面代码的第30行,为什么用some(ref mut remainder),而不是用some(&mut refmainder)?这是因为在进行some(&mut remainder)=self.remainder模式匹配时,remainder会被自动解引用成str类型,而不是可变的&str类型。     另一种写法 1impl iterator for strsplit { 2    // 迭代的结果也要与strsplit拥有相同的生命周期,是因为要在strsplit的成员remainder上做迭代。 3    type item = &'a str; 4 5    fn next(&mut self) -> option { 6        // 为什么不可以这么写??? 7        let remainder = &mut self.remainder?; 8        if let some(next_delim) = remainder.find(self.delimiter) { 9            let until_remainder = &remainder[..next_delim];10            *remainder = &remainder[next_delim + self.delimiter.len()..];11            some(until_remainder)12        }else {13            self.remainder.take()14        }15    }16}17  
在迭代器的next方法里尝试换一种写法,编译器检查通过,但是执行测试不通过。也就是上面代码的第7行,为什么不可以这么写?
self.remainder是option类型,这里的泛型是引用。所以在执行unwrap(),expect()或?时,会将option里的引用copy一份出来赋值给remainder,然后在这个新的remainder上作可变引用,而self.remainder没有任何变化。
我们可以使用option的as_mut()方法,因为它返回的是option:
1fn next(&mut self) -> option {2        // 为什么这么写不可以???3        // let remainder = &mut self.remainder?;45        let remainder = self.remainder.as_mut()?;6        ......7    }  
得到了一个self.remainder的可变引用,因此测试通过。
在下一篇文章中,我们通过更多的例子来继续学习rust的生命周期。  


阿布扎比港务公司正在采用区块链来提高航运与物流的效率
深度解析路虎全新发现底盘
使用Verilog/SystemVerilog硬件描述语言练习数字硬件设计
传苹果A7芯片今夏试生产 明年开始商业化量产
毫米波雷达的自适应波束成形技术:提升感知精度的前沿探索
CRust学习笔记:生命周期-1
如何解决制造企业数字化转型中的数据散乱和管理难题,实现顺利转型?
介绍一种基于功能核酸的微流控热泳生物传感器
《SUPERHOT VR》一周收入超200万美元 不支持与Oculus Rift交叉购买
太阳能+光伏浪涌保护器的选型方案
MG3633A大量特供MG3633A信号发生器/安立MG3633A
新版火车票二维码防伪技术
科沃斯商用在银行领域的落地应用策略
信号继电器作用是什么
VR、机器人、智能汽车:下一个独角兽会出自哪个风口?
低压断路器脱扣器的选择与整定
一大批前沿的科技产品都在向VR领域靠拢,VR大规模商业化爆发节点来临
适用于电流模式DC-DC转换器的统一的LTspice AC模型
MCU短缺正在下沉技术研发
新一代的USB 3.0外设控制器