一、什么是切片(Slice)
切片这个概念很形象,也很容易为人理解。正如一个面包,如果一个无法吃掉全部,那么给他切一片不正好么?既不浪费,又不会引起不必要的后果。这样比喻不是很贴切,但其实意思有些类似。切片其实是“一个双字 对象(two-word object),第一个字是一个指向数据的指针,第二个字是切片的长度。这 个 “字” 的宽度和 usize 相同,由处理器架构决定”,不过,切片的大小在编译时是无法固定的。一般可以认为切片是数组的一部分和或者全部的所有权,而且其是动态的,可以随时扩展。其实如果有c/c++的经验的人,可以简单的把Slice当成一个指针加数据长度的数据结构。
切片的目的非常单纯,就是实现动态的数组操作。
对于slice来说,首先,这个数据类型是没有所有权的。第二这个数据类型有长度和容量两个大小的属性,前者是真实的长度,后者是占有空间的大小长度。slice可以用在数据值参、字符串处理等具体的应用场景上。
二、应用例程
其实编程就是这样,道理讲好多回,不如把代码跑一回,人和人的理解能力以及表述能力都参差不齐,但拿出具体的代码一跑,就清楚了,这也是学计算机比学纯数学、物理要简单的一个重要原因,可以真实的看到场景。扯远了,还是先看一个slice的代码示例:
// slicing a Vec
let vec = vec![1, 2, 3];
let int_slice = &vec[..];
// coercing an array to a slice
let str_slice: &[&str] = &["one", "two", "three"];
再看一个传参的:
fn show(arr: &[u8]) {
for i in arr {
print!("{} ", i);
}
println!("");
}
fn main() {
let a: [u8; 3] = [1, 2, 3];
let slice_a = &a[..];
show(slice_a);
let b: [u8; 4] = [1, 2, 3, 4];
show(&b[..]);
}
三、字符串中的slice
为什么要把字符串单独拿出来说,主要原因是在Rust中有两种形式,一种是str ;另外一种 String。前者是Rust中的标准类型。而后者则是标准库提供的一个类型,可以理解成为了让字符串操作更方便的更高一层的抽象封装。而字符串切片表示成&str。或者说,字符串的字面值就是字符串的切片。
let mut s0 = String::from("mytest");
let sl = &s0[0..2];
s0.push_str("ok"); // rust的安全性就在此,部分被此乃后,不允许再改变。
println!("slice = {}", sl);
错误如下:
error[E0502]: cannot borrow `s0` as mutable because it is also borrowed as immutable
--> src\main.rs:4:5
|
3 | let sl = &s0[0..2];
| -- immutable borrow occurs here
4 | s0.push_str("yes!"); // rust的安全性就在此,部分被此乃后,不允许再改变。
| ^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
5 | println!("slice = {}", sl);
| -- immutable borrow later used here
error: aborting due to previous error
看一下编译错误,应该就明白怎么回事儿了。
四、总结
切片是个好东西,估计在c++中未来也有可能也会有类似的数据结构出现。切片在Rust中的应用,从某种程度上保证了变量应用的安全性,这也符合rust的风格,从安全的角度来看,各种语言都是殊途同归,目的是一样的,可能实现的手法类似或者各有千秋。一花独放不是春,百花齐放才是春。
努力吧,归来的少年!