本系列文章是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文章
4406浏览量
66812 -
生命周期
+关注
关注
0文章
18浏览量
7547 -
Rust
+关注
关注
1文章
240浏览量
7471
原文标题:CRust学习笔记:生命周期-2
文章出处:【微信号:Rust语言中文社区,微信公众号:Rust语言中文社区】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
基于Rust语言中的生命周期
KaihongOS操作系统:页面的生命周期介绍
ServiceAbility的生命周期介绍
AutoScaling 生命周期挂钩功能
HarmonyOS应用开发-PageAbility生命周期介
一文读懂Android Activity生命周期
CRust学习笔记:生命周期-1
CRust学习笔记:声明宏
CRust学习笔记:智能指针和内部可变性
鸿蒙开发:【PageAbility的生命周期】

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