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

电子说

1.3w人已加入

描述

本系列文章是Jon Gjengset发布的CRust of Rust系列视频的学习笔记,CRust of Rust是一系列持续更新的Rust中级教程。

在这篇文章中,我们将研究一个需要显式注释多个生命期的例子。我们还将讨论不同字符串类型之间的一些差异,以及在自定义的trait上引入泛型。这个例子是根据给定的字符串和分隔符对字符串进行拆分。

创建一个新项目:

cargo new --lib strsplit
  lib.rs中写入如下代码:
 1#[derive(Debug)]
 2pub struct StrSplit<'a> {
 3    remainder: &'a str, 
 4    delimiter: &'a str,
 5}
 6
 7#[allow(dead_code)]
 8impl<'a> StrSplit<'a> {
 9    pub fn new(haystack: &'a str, delimiter: &'a str) -> Self {
10        Self {
11            remainder: haystack,
12            delimiter,
13        }
14    }
15}
16
17impl<'a> Iterator for StrSplit<'a> {
18    type Item = &'a str;
19
20    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            None
27        }else {
28            let rest = self.remainder;
29            // 为什么空字符串可以赋值给self.remainder ???
30            self.remainder = "";
31            Some(rest)
32        }
33    }
34}
35
36#[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 tests
test str_split_1::it_works ... ok
test str_split_1::tail ... FAILED
    我们将struct StrSplit的成员remainder定义为Option<&'a str>类型来修复这个bug:
 1/**
 2 * StrSplit 与成员 remainder,delimiter 拥有相同的生命周期
 3 */
 4#[derive(Debug)]
 5pub struct StrSplit<'a> {
 6    // 使用Option
 7    remainder: Option<&'a str>, 
 8    delimiter: &'a str,
 9}
10
11#[allow(dead_code)]
12impl<'a> StrSplit<'a> {
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}
23
24impl<'a> Iterator for StrSplit<'a> {
25    // 迭代的结果也要与StrSplit拥有相同的生命周期,是因为要在StrSplit的成员remainder上做迭代。
26    type Item = &'a str;
27
28    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            None
40        }
41    }
42}
43
44#[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}
50
51#[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 tests
test str_split_2::tail ... ok
test str_split_2::it_works ... ok
      在上面代码的第30行,为什么用Some(ref mut remainder),而不是用Some(&mut refmainder)?这是因为在进行Some(&mut remainder)=self.remainder模式匹配时,remainder会被自动解引用成str类型,而不是可变的&str类型。     另一种写法
 1impl<'a> Iterator for StrSplit<'a> {
 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<&'a str>类型,这里的泛型是引用。所以在执行unwrap(),expect()或?时,会将Option里的引用Copy一份出来赋值给remainder,然后在这个新的remainder上作可变引用,而self.remainder没有任何变化。

我们可以使用Option的as_mut()方法,因为它返回的是Option<&mut T>:

 

1fn next(&mut self) -> Option {
2        // 为什么这么写不可以???
3        // let remainder = &mut self.remainder?;
4
5        let remainder = self.remainder.as_mut()?;
6        ......
7    }

 

得到了一个self.remainder的可变引用,因此测试通过。

在下一篇文章中,我们通过更多的例子来继续学习Rust的生命周期。  

  审核编辑:汤梓红

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分