Rust字符串切片:修订间差异
外观
Page creation by admin bot |
Page update by admin bot |
||
第1行: | 第1行: | ||
= Rust字符串切片 = | = Rust字符串切片 = | ||
'''Rust字符串切片'''(String Slice)是Rust所有权系统中处理字符串数据的重要概念,它提供了一种安全、高效的方式来引用字符串的一部分,而无需复制数据。字符串切片是Rust内存安全保证的核心机制之一。 | |||
'''Rust字符串切片'''(String | |||
== 基本概念 == | |||
字符串切片是一个指向字符串某部分的不可变引用,类型表示为<code>&str</code>。它由两部分组成: | |||
* 指向数据的指针 | |||
* 长度信息(以字节计) | |||
切片允许你引用集合中连续的元素序列,而不是整个集合。对于字符串而言,切片总是有效的UTF-8序列。 | |||
=== 语法表示 === | |||
字符串切片的语法形式为: | |||
<syntaxhighlight lang="rust"> | <syntaxhighlight lang="rust"> | ||
let slice = &string[start..end]; // 包含start,不包含end | |||
let slice = & | |||
</syntaxhighlight> | </syntaxhighlight> | ||
== 创建字符串切片 == | |||
=== 从String创建 === | |||
<syntaxhighlight lang="rust"> | |||
fn main() { | |||
let s = String::from("hello world"); | |||
let hello = &s[0..5]; // 或 &s[..5] | |||
let world = &s[6..11]; // 或 &s[6..] | |||
let whole = &s[..]; // 整个字符串切片 | |||
println!("{}", hello); // 输出: hello | |||
println!("{}", world); // 输出: world | |||
println!("{}", whole); // 输出: hello world | |||
} | |||
</syntaxhighlight> | |||
=== 字符串字面量就是切片 === | |||
字符串字面量实际上就是切片: | |||
<syntaxhighlight lang="rust"> | |||
let s: &str = "Hello, world!"; | |||
</syntaxhighlight> | |||
== 内存表示 == | == 内存表示 == | ||
<mermaid> | <mermaid> | ||
graph LR | graph LR | ||
A[ | A[String] -->|堆内存| B("hello world") | ||
C[&str hello] -->|引用| D("hello") | |||
E[&str world] -->|引用| F("world") | |||
</mermaid> | </mermaid> | ||
== 重要特性 == | |||
=== UTF-8安全 === | |||
Rust会确保字符串切片始终是有效的UTF-8序列。尝试创建无效的UTF-8切片会导致panic: | |||
<syntaxhighlight lang="rust"> | <syntaxhighlight lang="rust"> | ||
fn | fn main() { | ||
let s = String::from("Здравствуйте"); | |||
let slice = &s[0..1]; // panic! 因为'З'占2字节 | |||
} | } | ||
</syntaxhighlight> | |||
=== 不可变性 === | |||
字符串切片总是不可变的引用,这保证了内存安全: | |||
<syntaxhighlight lang="rust"> | |||
fn main() { | fn main() { | ||
let | let mut s = String::from("hello"); | ||
let slice = &s[..]; | |||
s.push_str(", world!"); // 错误!不能同时存在可变和不可变引用 | |||
println!("{}", slice); | |||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
== 实际应用案例 == | |||
< | |||
=== 函数参数 === | |||
</ | 使用<code>&str</code>作为函数参数比<code>String</code>更灵活: | ||
<syntaxhighlight lang="rust"> | <syntaxhighlight lang="rust"> | ||
fn | fn print_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() { | fn main() { | ||
let | let s = String::from("hello world"); | ||
let word = print_first_word(&s); | |||
println!("第一个单词: {}", word); // 输出: hello | |||
let literal = "foo bar"; | |||
let word = print_first_word(literal); | |||
println!("第一个单词: {}", word); // 输出: foo | |||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== 高效字符串处理 === | |||
避免不必要的复制: | |||
= | <syntaxhighlight lang="rust"> | ||
fn get_file_extension(filename: &str) -> Option<&str> { | |||
filename.split('.').last() | |||
} | |||
fn main() { | |||
let path = "archive.tar.gz"; | |||
match get_file_extension(path) { | |||
Some(ext) => println!("文件扩展名: {}", ext), | |||
None => println!("无扩展名"), | |||
} | |||
} | |||
</syntaxhighlight> | </syntaxhighlight> | ||
== 高级主题 == | |||
=== 生命周期关联 === | === 生命周期关联 === | ||
字符串切片与生命周期密切相关: | |||
<syntaxhighlight lang="rust"> | <syntaxhighlight lang="rust"> | ||
fn longest<'a>(s1: &'a str, s2: &'a str) -> &'a str { | fn longest<'a>(s1: &'a str, s2: &'a str) -> &'a str { | ||
if s1.len() > s2.len() { s1 } else { s2 } | if s1.len() > s2.len() { | ||
s1 | |||
} else { | |||
s2 | |||
} | |||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
== | === 与String的区别 === | ||
{| class="wikitable" | {| class="wikitable" | ||
|- | |- | ||
! 特性 !! String !! &str | |||
|- | |- | ||
| | | 所有权 || 拥有 || 借用 | ||
|- | |- | ||
| | | 大小 || 可变 || 固定 | ||
|- | |||
| 存储位置 || 堆 || 可以是堆或静态内存 | |||
|- | |||
| 可变性 || 可变 || 不可变 | |||
|} | |} | ||
== | == 常见错误及解决 == | ||
=== 切片越界 === | |||
尝试超出字符串范围的切片会导致panic: | |||
<syntaxhighlight lang="rust"> | |||
let s = "hello"; | |||
let slice = &s[0..10]; // panic! | |||
</syntaxhighlight> | |||
解决方案:使用<code>s.len()</code>检查长度或捕获错误。 | |||
=== 无效UTF-8 === | |||
如前所述,必须确保切片边界在UTF-8字符边界上。可以使用<code>char_indices()</code>方法安全处理: | |||
<syntaxhighlight lang="rust"> | |||
fn safe_slice(s: &str, start: usize, end: usize) -> Option<&str> { | |||
let mut char_pos = 0; | |||
let mut byte_start = None; | |||
let mut byte_end = None; | |||
for (i, (byte_pos, _)) in s.char_indices().enumerate() { | |||
if i == start { | |||
byte_start = Some(byte_pos); | |||
} | |||
if i == end { | |||
byte_end = Some(byte_pos); | |||
break; | |||
} | |||
} | |||
match (byte_start, byte_end) { | |||
(Some(s), Some(e)) => Some(&s[s..e]), | |||
_ => None, | |||
} | |||
} | |||
</syntaxhighlight> | |||
== 性能考虑 == | |||
字符串切片的优势在于: | |||
* 零成本抽象 - 不涉及堆分配 | |||
* 避免了数据复制 | |||
* 编译时检查保证安全性 | |||
数学上,切片的访问时间复杂度是<math>O(1)</math>,因为它只是指针操作。 | |||
== 总结 == | == 总结 == | ||
Rust字符串切片是: | |||
* 对字符串部分内容的引用 | |||
* 类型为<code>&str</code> | |||
* 保证内存安全和UTF-8有效性 | |||
* 高效且灵活,适合作为函数参数 | |||
* 与Rust所有权系统深度集成 | |||
掌握字符串切片是理解Rust内存模型的关键步骤,也是编写高效、安全Rust代码的基础。 | |||
[[Category:编程语言]] | [[Category:编程语言]] | ||
[[Category:Rust]] | [[Category:Rust]] | ||
[[Category: | [[Category:Rust所有权系统]] |
2025年5月2日 (五) 00:24的最新版本
Rust字符串切片[编辑 | 编辑源代码]
Rust字符串切片(String Slice)是Rust所有权系统中处理字符串数据的重要概念,它提供了一种安全、高效的方式来引用字符串的一部分,而无需复制数据。字符串切片是Rust内存安全保证的核心机制之一。
基本概念[编辑 | 编辑源代码]
字符串切片是一个指向字符串某部分的不可变引用,类型表示为&str
。它由两部分组成:
- 指向数据的指针
- 长度信息(以字节计)
切片允许你引用集合中连续的元素序列,而不是整个集合。对于字符串而言,切片总是有效的UTF-8序列。
语法表示[编辑 | 编辑源代码]
字符串切片的语法形式为:
let slice = &string[start..end]; // 包含start,不包含end
创建字符串切片[编辑 | 编辑源代码]
从String创建[编辑 | 编辑源代码]
fn main() {
let s = String::from("hello world");
let hello = &s[0..5]; // 或 &s[..5]
let world = &s[6..11]; // 或 &s[6..]
let whole = &s[..]; // 整个字符串切片
println!("{}", hello); // 输出: hello
println!("{}", world); // 输出: world
println!("{}", whole); // 输出: hello world
}
字符串字面量就是切片[编辑 | 编辑源代码]
字符串字面量实际上就是切片:
let s: &str = "Hello, world!";
内存表示[编辑 | 编辑源代码]
重要特性[编辑 | 编辑源代码]
UTF-8安全[编辑 | 编辑源代码]
Rust会确保字符串切片始终是有效的UTF-8序列。尝试创建无效的UTF-8切片会导致panic:
fn main() {
let s = String::from("Здравствуйте");
let slice = &s[0..1]; // panic! 因为'З'占2字节
}
不可变性[编辑 | 编辑源代码]
字符串切片总是不可变的引用,这保证了内存安全:
fn main() {
let mut s = String::from("hello");
let slice = &s[..];
s.push_str(", world!"); // 错误!不能同时存在可变和不可变引用
println!("{}", slice);
}
实际应用案例[编辑 | 编辑源代码]
函数参数[编辑 | 编辑源代码]
使用&str
作为函数参数比String
更灵活:
fn print_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 s = String::from("hello world");
let word = print_first_word(&s);
println!("第一个单词: {}", word); // 输出: hello
let literal = "foo bar";
let word = print_first_word(literal);
println!("第一个单词: {}", word); // 输出: foo
}
高效字符串处理[编辑 | 编辑源代码]
避免不必要的复制:
fn get_file_extension(filename: &str) -> Option<&str> {
filename.split('.').last()
}
fn main() {
let path = "archive.tar.gz";
match get_file_extension(path) {
Some(ext) => println!("文件扩展名: {}", ext),
None => println!("无扩展名"),
}
}
高级主题[编辑 | 编辑源代码]
生命周期关联[编辑 | 编辑源代码]
字符串切片与生命周期密切相关:
fn longest<'a>(s1: &'a str, s2: &'a str) -> &'a str {
if s1.len() > s2.len() {
s1
} else {
s2
}
}
与String的区别[编辑 | 编辑源代码]
特性 | String | &str |
---|---|---|
所有权 | 拥有 | 借用 |
大小 | 可变 | 固定 |
存储位置 | 堆 | 可以是堆或静态内存 |
可变性 | 可变 | 不可变 |
常见错误及解决[编辑 | 编辑源代码]
切片越界[编辑 | 编辑源代码]
尝试超出字符串范围的切片会导致panic:
let s = "hello";
let slice = &s[0..10]; // panic!
解决方案:使用s.len()
检查长度或捕获错误。
无效UTF-8[编辑 | 编辑源代码]
如前所述,必须确保切片边界在UTF-8字符边界上。可以使用char_indices()
方法安全处理:
fn safe_slice(s: &str, start: usize, end: usize) -> Option<&str> {
let mut char_pos = 0;
let mut byte_start = None;
let mut byte_end = None;
for (i, (byte_pos, _)) in s.char_indices().enumerate() {
if i == start {
byte_start = Some(byte_pos);
}
if i == end {
byte_end = Some(byte_pos);
break;
}
}
match (byte_start, byte_end) {
(Some(s), Some(e)) => Some(&s[s..e]),
_ => None,
}
}
性能考虑[编辑 | 编辑源代码]
字符串切片的优势在于:
- 零成本抽象 - 不涉及堆分配
- 避免了数据复制
- 编译时检查保证安全性
数学上,切片的访问时间复杂度是,因为它只是指针操作。
总结[编辑 | 编辑源代码]
Rust字符串切片是:
- 对字符串部分内容的引用
- 类型为
&str
- 保证内存安全和UTF-8有效性
- 高效且灵活,适合作为函数参数
- 与Rust所有权系统深度集成
掌握字符串切片是理解Rust内存模型的关键步骤,也是编写高效、安全Rust代码的基础。