数组、字符串,本质上是一样的,都是对应一片连续的内存。在 C 语言中,字符串就是以数字零结尾的 unsigned char 数组,也就是 Rust 的 u8 数组。
C 语言的数组,就是一个指针。至于数组的大小,交给程序员自己去管理。这样做的优点是,语言的语法机制很简单,也很容易理解。但多年用下来,发现缺点也是致命的,这就是所谓的内存泄露和野指针问题。据微软统计,软件开发中 70% 的缺陷是这一机制所致。
既然如此,Rust 应运而生,立志要解决这一问题。思路基本如下:
1. 切片
Rust 的指针至少包含数据的地址、大小两个参数,这种指针称为 slice,即切片。
- Rust 的数组就是切片。
- Rust 的 &str 也是切片——一个指向固定长度字符串的切片。
当然我们会好奇,str 是个什么鸟类型?从语法的角度看,Rust 中的字符串 "ABCDEFG" 包括了指向该数据的地址和长度,str 应该就是 ABCDEFG 这七个字母代表的数据存储区本身。但是这个单纯的数据存储区本身,在 Rust 中无法用一个确切的数据类型表达的,所以只能用 &str 这个切片来间接访问这个数据区。
2. 动态数组
切片的问题是其大小无法改变,因此,无法对切片内的数据元素进行添加或者删除操作。于是,在 Rust 中真正有实用价值的数组机制是动态数组。
动态数组比切片多了一个数据存储区容量 —— Capacity。如果动态数组大小在 Capacity 范围内变化,则数据存储区位置无需改变,仅需调整数据大小 size 或 len 即可。如果数据存储区不够用,则再申请一块更大的空间,把现有数据转移过去,再继续操作。
在 Rust 中,对于一般数据来讲,动态数组就是数据类型 Vec;对于字符串来讲,动态大小的字符串就是数据类型 String。
3. 数据类型转换
数据类型之间的转换方法参见下表,加粗的文字的内容表示类型之间只能间接转换
源数据 | -> [u8] | -> Vec<u8> | -> &str | -> String |
---|---|---|---|---|
[u8] | - | Vec::from(x) | std::str::from_utf8(&x).unwrap() |
String::from_utf8(x.to_vec()).unwrap() |
Vec<u8> | &x | - | std::str::from_utf8(&x).unwrap() |
String::from_utf8(x).unwrap() |
&str | x.as_bytes() | x.as_bytes().to_vec() |
- | x.to_string() |
String | x.as_bytes() |
x.as_bytes().to_vec() |
&x | - |
全部示例代码如下:
fn main() {let a: [u8; 3] = [65, 66, 67];let b: Vec<u8> = Vec::from(a);let c: &str = std::str::from_utf8(&a).unwrap();let d: String = String::from_utf8(a.to_vec()).unwrap();println!();println!("a={:?}", a);println!("b={:?}", b);println!("c={:?}", c);println!("d={:?}", d);let b: Vec<u8> = vec![65, 66, 67];let a = &b;let c: &str = std::str::from_utf8(&b).unwrap();let d: String = String::from_utf8(b.clone()).unwrap();println!();println!("a={:?}", a);println!("b={:?}", b);println!("c={:?}", c);println!("d={:?}", d);let c: &str = "ABC";let a = c.as_bytes();let b: Vec<u8> = c.as_bytes().to_vec();let d: String = c.to_string();println!();println!("a={:?}", a);println!("b={:?}", b);println!("c={:?}", c);println!("d={:?}", d);let d: String = String::from("ABC");let a = d.as_bytes();let b: Vec<u8> = d.as_bytes().to_vec();let c: &str = &d;println!();println!("a={:?}", a);println!("b={:?}", b);println!("c={:?}", c);println!("d={:?}", d);
}run ...a=[65, 66, 67]
b=[65, 66, 67]
c="ABC"
d="ABC"a=[65, 66, 67]
b=[65, 66, 67]
c="ABC"
d="ABC"a=[65, 66, 67]
b=[65, 66, 67]
c="ABC"
d="ABC"a=[65, 66, 67]
b=[65, 66, 67]
c="ABC"
d="ABC"