0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

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

jf_wN0SrCdH 来源:coding到灯火阑珊 作者:李明 2022-12-19 09:34 次阅读

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

让我们接着上一篇文章继续学习Rust的生命周期。在上一篇文章中的代码基础上,加入如下的函数和测试用例:

 1fnuntil_char(s:&str,c:char)->&str{
 2StrSplit::new(s,&format!("{}",c))
 3.next()
 4.expect("StrSplitalwaysgivesatleastoneresult")
 5}
 6
 7#[test]
 8fnuntil_char_test(){
 9assert_eq!(until_char("helloworld",'o'),"hell");
10}
编译器会报如下错误:
error[E0515]: cannot return value referencing temporary value

这里的临时值是&format!("{}",c),从代码中可以看出,参数s、c和next()之后的值要拥有相同的生命周期,因此返回值与临时值的生命周期相同。但是这个临时值的生命周期在函数执行完后就结束了,所以编译不通过。

有一种解决办法是使用String

1#[derive(Debug)]
2pubstructStrSplit<'a>{
3//使用Option
4remainder:Option<&'a str>,
5delimiter:String,
6}
但是使用String是有两个问题的,我们先来比较一下str,&str,String之间的区别:

str -> [char]:相当于char类型的切片,既可以分配在栈上,也可以分配在堆上,还可以分配在static区。

&str -> &[char]:相当于胖指针,包含指向str的指针和字符串的长度。

String -> Vec分配在堆上的字符向量,在栈上有一个胖指针指向这个堆上的字符向量。

String转换&str相对容易一些,因为已知字符串的起始位置及长度。而&str转换成String就复杂一些,需要先在堆上分配一段空间,然后再通过内存拷贝(memcpy)把字符copy到堆上。

因此使用String的第一个问题是性能问题;第二个问题是不能兼容嵌入式系统,大多数嵌入式系统没有堆内存。

我们选择更好的解决方案,定义多个生命周期

1#[derive(Debug)]
2pubstructStrSplit<'haystack, 'delimiter>{
3//使用Option
4remainder:Option<&'haystack str>,
5delimiter:&'delimiterstr,
6}
 1impl<'haystack, 'delimiter>StrSplit<'haystack, 'delimiter>{
 2/**
 3*新构建的StrSplit与传入的参数haystack,delimiter拥有相同的生命周期
 4*/
 5pubfnnew(haystack:&'haystackstr,delimiter:&'delimiterstr)->Self{
 6Self{
 7remainder:Some(haystack),
 8delimiter,
 9}
10}
11}
12
13impl<'haystack>IteratorforStrSplit<'haystack, '_>{
14//迭代的结果也要与StrSplit拥有相同的生命周期,是因为要在StrSplit的成员remainder上做迭代。
15typeItem=&'haystackstr;
16
17fnnext(&mutself)->Option{
18letremainder=self.remainder.as_mut()?;
19ifletSome(next_delim)=remainder.find(self.delimiter){
20letuntil_remainder=&remainder[..next_delim];
21*remainder=&remainder[next_delim+self.delimiter.len()..];
22Some(until_remainder)
23}else{
24self.remainder.take()
25}
26}
27}
执行cargo test,测试通过。 泛型化Delimiter 在这里我们将分隔符进行泛型化,使得StrSplit更加通用。
 1pubstructStrSplit<'haystack, D>{
 2//使用Option
 3remainder:Option<&'haystack str>,
 4delimiter:D,
 5}
 6
 7impl<'haystack, D>StrSplit<'haystack, D>{
 8pubfnnew(haystack:&'haystackstr,delimiter:D)->Self{
 9Self{
10remainder:Some(haystack),
11delimiter,
12}
13}
14}
定义一个trait,包含一个find_next()方法,用于返回分隔符在字符串中的起始位置和结束位置
1pubtraitDelimiter{
2//返回分隔符在字符串中的起始位置和结束位置
3fnfind_next(&self,s:&str)->Option<(usize, usize)>;
4}
迭代器修改如下:
 1impl<'haystack, D>IteratorforStrSplit<'haystack, D>
 2where
 3D:Delimiter
 4{
 5//迭代的结果也要与StrSplit拥有相同的生命周期,是因为要在StrSplit的成员remainder上做迭代。
 6typeItem=&'haystackstr;
 7
 8fnnext(&mutself)->Option{
 9letremainder=self.remainder.as_mut()?;
10ifletSome((delim_start,delim_end))=self.delimiter.find_next(remainder){
11letuntil_remainder=&remainder[..delim_start];
12*remainder=&remainder[delim_end..];
13Some(until_remainder)
14}else{
15self.remainder.take()
16}
17}
18}
然后为&str和char分别实现Delimiter trait:
 1implDelimiterfor&str{
 2fnfind_next(&self,s:&str)->Option<(usize, usize)>{
 3s.find(self).map(|start|(start,start+self.len()))
 4}
 5}
 6
 7implDelimiterforchar{
 8fnfind_next(&self,s:&str)->Option<(usize, usize)>{
 9s.char_indices()
10.find(|(_,c)|c==self)
11.map(|(start,_)|(start,start+self.len_utf8()))
12}
13}
函数until_char()修改为:
1pubfnuntil_char(s:&str,c:char)->&str{
2StrSplit::new(s,c)
3.next()
4.expect("StrSplitalwaysgivesatleastoneresult")
5}
执行cargo test,测试通过。

审核编辑:汤梓红

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

    关注

    3

    文章

    3868

    浏览量

    61309
  • 生命周期
    +关注

    关注

    0

    文章

    15

    浏览量

    7437
  • Rust
    +关注

    关注

    1

    文章

    223

    浏览量

    6387

原文标题:CRust学习笔记:生命周期-2

文章出处:【微信号:Rust语言中文社区,微信公众号:Rust语言中文社区】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    基于Rust语言中的生命周期

    Rust是一门系统级编程语言具备高效、安和并发等特,而生命周期是这门语言中比较重要的概念之一。在这篇教程中,我们会了解什么是命周期、为什么需要生命周期、如何使用生命周期,同时我们依然会
    的头像 发表于 09-19 17:03 602次阅读

    AutoScaling 生命周期挂钩功能

    摘要: AutoScaling 伸缩组实例管理功能全面升级,新上线生命周期挂钩(LifecycleHook)功能,方便用户更加灵活地管理伸缩组内实例。使用生命周期挂钩可以在伸缩组发生伸缩活动时将伸缩
    发表于 06-27 17:13

    HarmonyOS应用开发-PageAbility生命周期

    pageAbility的生命周期如下图所示:在代码中通过调用下列方法实现生命周期操作:onShow() :Ability由后台不可见状态切换到前台可见状态调用onShow方法,此时用户在屏幕可以看到
    发表于 10-17 11:11

    在S32G2 RM中有“生命周期”,生命周期的完整含义是什么?

    在S32G2 RM中,有“生命周期”。生命周期的完整含义是什么,我们应该如何使用它?
    发表于 04-23 10:37

    贯穿于全生命周期的功能安全

    简要介绍了功能安全在SIS 全安全生命周期的主要活动,叙述了全生命周期的功能安全管理。简要阐述了贯穿于整体安全生命周期的功能安全进行的主要阶段,同时提出了在设计SIS
    发表于 12-19 15:50 15次下载

    贯穿于全生命周期的功能安全

    简要介绍了功能安全在SIS 全安全生命周期的主要活动,叙述了全生命周期的功能安全管理。简要阐述了贯穿于整体安全生命周期的功能安全进行的主要阶段,同时提出了在设计SIS 时
    发表于 01-06 17:11 6次下载

    一文读懂Android Activity生命周期

    正常情况下Activity的生命周期: Activity的生命周期大概可以归为三部分 整个的生命周期:onCreate()可以设置所有的“全局”状态, onDestory()可以释放所有的资源 可见
    发表于 05-30 01:03 1370次阅读

    Synopsys 启动硅生命周期管理计划

    Synopsis 的数据分析驱动的硅生命周期管理计划解决了 IC 生命周期中的质量、可靠性和安全挑战。
    发表于 08-18 15:37 628次阅读
    Synopsys 启动硅<b class='flag-5'>生命周期</b>管理计划

    CRust学习笔记生命周期-1

    本系列文章是Jon Gjengset发布的CRust of Rust系列视频的学习笔记CRust of Rust是一系列持续更新的Rust中级教程。
    的头像 发表于 12-19 09:33 560次阅读

    CRust学习笔记:声明宏

    本系列文章是Jon Gjengset发布的CRust of Rust系列视频的学习笔记CRust of Rust是一系列持续更新的Rust中级教程。
    的头像 发表于 01-06 14:37 523次阅读

    CRust学习笔记:智能指针和内部可变性

    本系列文章是Jon Gjengset发布的CRust of Rust系列视频的学习笔记CRust of Rust是一系列持续更新的Rust中级教程。
    的头像 发表于 01-29 14:58 556次阅读

    Vue入门Vue的生命周期

    .生命周期 4.1生命周期是什么 Vue的生命周期, 就是Vue实例从创建到销毁的过程.
    的头像 发表于 02-06 16:16 659次阅读
    Vue入门Vue的<b class='flag-5'>生命周期</b>

    编译器的标准生命周期

    编译器的标准生命周期
    发表于 03-14 19:06 0次下载
    编译器的标准<b class='flag-5'>生命周期</b>

    编译器的标准生命周期

    编译器的标准生命周期
    发表于 07-05 19:32 0次下载
    编译器的标准<b class='flag-5'>生命周期</b>

    数据包的生命周期

    电子发烧友网站提供《数据包的生命周期.pdf》资料免费下载
    发表于 10-13 14:44 0次下载