本文最后更新于 2024年6月28日 中午
切片(Slices)是引用的一种,用来引用集合中连续的一串元素。
假想一个场景,一个字符串处理函数,函数需要将字符串按照空格分隔,同时返回字符串中第一个单词。
在没有切片的情况下,应该如何实现?下面是一个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 fn first_word (s: &String ) -> usize { let bytes = s.as_bytes (); for (i, &item) in bytes.iter ().enumerate () { if item == b' ' { return i; } } s.len () }fn main () { let mut s = String ::from ("hello world" ); let word = first_word (&s); s.clear (); }
上述代码可以实现相应功能,但是需要人工同步索引值和字符串,这耗费精力也容易出错。使用Rust字符串切片可以解决同步索引值和字符串问题。
字符串切片 字符串切片是String
的部分引用,它的声明像下面这样:
1 2 3 4 let s = String ::from ("hello world" );let hello = &s[0 ..5 ];let world = &s[6 ..11 ];
hello
是s
字符串的一部分,范围由[0..5]
明确给出。范围的形式可以理解为[starting_index..ending_index]
,starting_index
是切片的第一个元素位置,ending_index
比切片中最后一个元素位置大1,可以理解为左闭右开
。下面图片是部分引用和字符串的区别:
还有一些范围的简写方式,请看代码:
1 2 3 4 5 6 7 8 9 let s = String ::from ("hello" ); let slice1 = &s[0 ..2 ]; let slice2 = &s[..2 ]; let len = s.len ();let slice3 = &s[0 ..len]; let slice4 = &s[..];
使用切片对字符串分割函数重写:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 fn first_word (s: &String ) -> &str { let bytes = s.as_bytes (); for (i, &item) in bytes.iter ().enumerate () { if item == b' ' { return &s[0 ..i]; } } &s[..] }fn main () { let mut s = String ::from ("hello world" ); let word = first_word (&s); s.clear (); println! ("the first word is: {word}" ); println! ("the first word is: {word}" ); s.clear (); }
在打印word
之前,若对字符串修改,编译将无法通过。使用字符串切片,编译器能够帮助完成文章开头提及的同步问题,只要编译能通过,那么字符串引用一定有效。
其他类型切片 字符串切片针对的是字符串类型,当然还有更通用的切片类型,考虑这个数组:
1 2 3 4 5 let a = [1 ,2 ,3 ,4 ,5 ];let slice = &a[1 ..3 ];assert_eq! (slice,&[2 ,3 ]);
这个切片类型为&[i32]
,它的工作原理和字符串切片一样,保存第一个元素的引用和长度。其余所有类型的切片都与这个切片类似。
参考 Rust docs