|
|
|
|
# Slice Type
|
|
|
|
|
|
|
|
|
|
切片可以引用集合中连续的元素序列,而不是整个集合。**切片是一种引用,因此他没有所有权。**
|
|
|
|
|
|
|
|
|
|
## 不使用切片找单词
|
|
|
|
|
|
|
|
|
|
下面的方法可以获取到第一个单词结尾的下标,但此值是独立于String的,所以不能保证他将来有效。
|
|
|
|
|
|
|
|
|
|
![image-20230104135420678](C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20230104135420678.png)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
将String转为字节数组。
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
let bytes = s.as_bytes();
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
使用迭代器遍历字节数组,enumerate包装了结果iter并将每个元素作为元组的一部分返回。第一个元素为索引,第二个元素为该元素的引用。
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
for (i, &item) in bytes.iter().enumerate(){}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 获取结果后修改字符串
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
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); // word will get the value 5
|
|
|
|
|
|
|
|
|
|
s.clear(); // this empties the String, making it equal to ""
|
|
|
|
|
|
|
|
|
|
// word still has the value 5 here, but there's no more string that
|
|
|
|
|
// we could meaningfully use the value 5 with. word is now totally invalid!
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
此种情况下,若想通过获取的下标值配合字符串获取第一个单词便会出错。
|
|
|
|
|
|
|
|
|
|
![image-20230104145801151](C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20230104145801151.png)
|
|
|
|
|
|
|
|
|
|
## String Slices
|
|
|
|
|
|
|
|
|
|
字符串*切片*是对字符串一部分的引用。
|
|
|
|
|
|
|
|
|
|
![image-20230104155425732](C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20230104155425732.png)
|
|
|
|
|
|
|
|
|
|
![image-20230104155706354](C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20230104155706354.png)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
一些切片的简便写法:
|
|
|
|
|
|
|
|
|
|
1. 如果你想从索引 0 开始,你可以删除两个句点之前的值。
|
|
|
|
|
2. 如果您的切片包含 的最后一个字节`String`,您可以删除尾随数字。
|
|
|
|
|
|
|
|
|
|
3. 可以删除这两个值以获取整个字符串的一部分。
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
let s = String::from("hello");
|
|
|
|
|
let len = s.len();
|
|
|
|
|
// 以下相等
|
|
|
|
|
let slice = &s[0..2];
|
|
|
|
|
let slice = &s[..2];
|
|
|
|
|
|
|
|
|
|
// 以下相等
|
|
|
|
|
let slice = &s[3..len];
|
|
|
|
|
let slice = &s[3..];
|
|
|
|
|
|
|
|
|
|
// 以下相等
|
|
|
|
|
let slice = &s[0..len];
|
|
|
|
|
let slice = &s[..];
|
|
|
|
|
let slice = &s; // 当参数为&str时,二者相同;当参数为&String时,只能传&s
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 修改后的first_word
|
|
|
|
|
|
|
|
|
|
![image-20230105091042697](C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20230105091042697.png)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
![image-20230105091209645](C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20230105091209645.png)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
![image-20230105091306505](C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20230105091306505.png)
|
|
|
|
|
|
|
|
|
|
**借用规则,如果我们有一个对某物的不可变引用,我们就不能同时使用一个可变引用**。我认为就是被借用后,未使用完就不可以修改。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### String Slices as Parameters
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
fn first_word(s: &String) -> &str {
|
|
|
|
|
|
|
|
|
|
fn first_word(s: &str) -> &str {
|
|
|
|
|
|
|
|
|
|
第一个函数的参数是一个 String 类型的引用,返回值是一个字符串 slice。第二个函数的参数是一个字符串 slice,返回值也是一个字符串 slice。
|
|
|
|
|
这两个函数在功能上是类似的,但它们处理的数据类型不同。第一个函数传入的是一个 String 类型的值,而第二个函数传入的是一个字符串 slice。
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
在需要字符串为参数时,更好的选择是使用**字符串切片**为参数,它允许我们对`&String`值和`&str`值使用相同的函数。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
![image-20230105094853847](C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20230105094853847.png)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
传切片
|
|
|
|
|
|
|
|
|
|
![image-20230105094954607](C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20230105094954607.png)
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
fn first_word(s: &str) -> &str {
|
|
|
|
|
let bytes = s.as_bytes();
|
|
|
|
|
|
|
|
|
|
for (i, &item) in bytes.iter().enumerate() {
|
|
|
|
|
if item == b' ' {
|
|
|
|
|
return &s[0..i];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
&s[..]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
let my_string = String::from("hello world");
|
|
|
|
|
|
|
|
|
|
// `first_word` works on slices of `String`s, whether partial or whole
|
|
|
|
|
let word = first_word(&my_string[0..6]);
|
|
|
|
|
let word = first_word(&my_string[..]);
|
|
|
|
|
// `first_word` also works on references to `String`s, which are equivalent
|
|
|
|
|
// to whole slices of `String`s
|
|
|
|
|
let word = first_word(&my_string);
|
|
|
|
|
|
|
|
|
|
let my_string_literal = "hello world";
|
|
|
|
|
|
|
|
|
|
// `first_word` works on slices of string literals, whether partial or whole
|
|
|
|
|
let word = first_word(&my_string_literal[0..6]);
|
|
|
|
|
let word = first_word(&my_string_literal[..]);
|
|
|
|
|
|
|
|
|
|
// Because string literals *are* string slices already,
|
|
|
|
|
// this works too, without the slice syntax!
|
|
|
|
|
let word = first_word(my_string_literal);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
![image-20230105105152810](C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20230105105152810.png)
|
|
|
|
|
|
|
|
|
|
在let s = "hello world"时,创建的是字符串字面量。使用 `from` 函数创建的字符串和字符串字面量是不同的东西。前者是**堆**上的字符串,后者是**常量**存储在程序二进制文件中的字符串。如果你将一个 `&` 引用传递给字符串字面量,它会被解释为字符串 slice。但是,如果你将 `&` 引用传递给通过 `from` 函数创建的字符串,它将会被解释为**字符串类型的引用**。
|
|
|
|
|
|
|
|
|
|
这是因为字符串字面量本身就是字符串 slice,所以也可以直接调用 first_word 函数,无需使用 slice 语法。
|
|
|
|
|
|
|
|
|
|
如果你传入了一个字符串字面量的引用,比如 &"hello world",那么它将会被转化为一个字符串 slice 类型,即 &str。所以,传入 &"hello world" 与传入 "hello world" 是等价的。
|
|
|
|
|
|
|
|
|
|
#### 使用from创建的字符串与字符串字面量
|
|
|
|
|
|
|
|
|
|
使用 `String::from` 创建的字符串是在堆上的动态内存。使用 `&` 获取这个字符串的引用,返回的是一个字符串 slice,而不是字符串字面量。
|
|
|
|
|
|
|
|
|
|
字符串字面量是编译时就被确定的**常量**字符串。使用 `let s = "str"` 定义一个字符串字面量,这个字符串存储在程序的**只读数据段**中,可以直接使用。使用 `&` 取出来的依然是字符串字面量。字符串字面量是slice的一种特殊情况。其值是字符串字面量的地址(只读内存区域)。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Other Slices
|
|
|
|
|
|
|
|
|
|
字符串切片是特定与字符串的。但其他集合也可以使用切片。
|
|
|
|
|
|
|
|
|
|
![image-20230105111754222](C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20230105111754222.png)
|
|
|
|
|
|
|
|
|
|
`{:?}` 是格式化输出中的一种控制符,用于输出一个值的“调试形式”。 在本例中,使用 `{:?}` 会输出 `slice` 值的调试形式,这意味着在输出的序列中会包含所有元素的值,以及切片的边界。"{:#?}"是"Debug"格式化输出中的一个选项。它输出更好看的、更具可读性的调试输出。这种输出格式适用于较为复杂的数据结构。"{:?}"是"Debug"格式化输出中的另一个选项。它输出的内容与"{:#?}"的输出类似,但是在行与行之间没有缩进。由于"{:?}"没有缩进,输出的信息可能不是那么好看,但是也更加紧凑。所以,"{:#?}"和"{:?}"的主要区别在于输出的可读性和紧凑性之间的权衡。
|
|
|
|
|
|
|
|
|
|
控制符包括但不限于:
|
|
|
|
|
|
|
|
|
|
- `{}`:按照默认方式输出值。
|
|
|
|
|
- `{:b}`:以二进制输出值。
|
|
|
|
|
- `{:o}`:以八进制输出值。
|
|
|
|
|
- `{:x}`:以十六进制输出值。
|
|
|
|
|
- `{:e}`:以科学计数法输出值。
|
|
|
|
|
|
|
|
|
|
在 Rust 中,数组的数据可以存储在堆或者栈上,具体取决于数组的大小。如果数组的大小是固定的,那么数组的数据就会存储在栈上;如果数组的大小是可变的,那么数组的数据就会存储在堆上。
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
// 栈上
|
|
|
|
|
fn main() {
|
|
|
|
|
let a = [1, 2, 3, 4, 5];
|
|
|
|
|
let b: [i32; 5] = [1, 2, 3, 4, 5];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 堆上
|
|
|
|
|
fn main() {
|
|
|
|
|
let c = vec![1, 2, 3, 4, 5];
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|