rust-str-string
新建字符串
String::new()
返回一个新的空字符串。这时还没有在堆上分配缓冲区,但将来会按需分配
String::with_capacity(n)
返回一个新的空字符串,其中预先分配了一个足以容纳至少 n 字节的缓冲区。
str_slice.to_string()
fn main() {
let s = "hello world";
let s1:String = s.to_string();
println!("{}", s1);
}
iter.collect()
fn main() {
let s = "hello world";
let s1:String = s.chars().filter(|x| !x.is_ascii_whitespace()).collect();
println!("{}", s1);
}
slice.to_owned()
效果和to_string()一样,to_owned会从借用转为拥有(重新clone一份)
fn main() {
let s = "hello world";
let s1:String = s.to_owned();
println!("{}", s1);
println!("{}", s);
}
判断&str内容
slice.len()
返回字符串的长度
fn main() {
let s = "hello world";
println!("{}", s.len());
}
slice.is_empty()
判断字符串是否为空,返回bool值
fn main() {
let s = "hello world";
println!("{}", s.is_empty());
}
slice.split_at(i)
返回元组,这会返回 (slice[..i], slice[i..])
fn main() {
let s = "hello world";
println!("{:?}", s.split_at(3));
}
slice.is_char_boundary(i)
判断索引i处是否是字符边界
字符边界 = 字符开始的位置
fn main() {
// 对于ascii来说,每一个就是一个字符
let s = "hello";
println!("{}", s.is_char_boundary(0)); // true
println!("{}", s.is_char_boundary(4)); // true
println!("{}", s.is_char_boundary(5)); // true(结尾也是边界)
println!("{}", s.is_char_boundary(11)); // false(越界)
}
fn main() {
// 对于utf-8来说,一个字符可能占一个字节,也可能占多个字节
// 中文占用3个字节,你->0-2,好->3-5
let s = "你好";
println!("{}", s.is_char_boundary(0)); // true
println!("{}", s.is_char_boundary(1)); // false
println!("{}", s.is_char_boundary(2)); // false
println!("{}", s.is_char_boundary(3)); // true
println!("{}", s.is_char_boundary(4)); // false
println!("{}", s.is_char_boundary(5)); // false
println!("{}", s.is_char_boundary(6)); // true
}
追加文本与插入文本
string.push(char)
将字符char追加到 string 的末尾。
fn main() {
let mut s = String::from("你好");
s.push('😁');
println!("{}", s); // 你好😁
}
string.push_str(slice)
追加 slice 的全部内容
fn main() {
let mut s = String::from("你好");
s.push_str("!!!");
println!("{}", s); // 你好!!!
}
string.extend(iter)
追加 iter 的全部内容
fn main() {
let mut s = String::from("你好");
let a = vec!['a', ' ', 'b'];
s.extend(a);
println!("{}", s); // 你好a b
}
string.insert(i, ch)
将char插入到指定的索引i处
需要插入在字符边界,否者会报错
fn main() {
let mut s = String::from("你好");
s.insert(3, 'A');
println!("{}", s);
}
string.insert_str(i, slice)
将&str插入到指定的索引i处
需要插入在字符边界,否者会报错
fn main() {
let mut s = String::from("你好");
s.insert_str(3, "哎哎哎");
println!("{}", s); // 你哎哎哎好
}
String + &str
Add<&str> 和 AddAssign<&str>
Sting + &str, 不能&str + String
fn main() {
let mut s = String::from("你好");
s = s + "!";
println!("{}", s);
}
移除文本与替换文本
String.clear()
清空字符串内容
fn main() {
let mut s = String::from("你好");
s.clear();
println!("{}", s);
}
string.truncate(i)
截断字符串,截断到索引i处(i必须是字符边界)
fn main() {
let mut s = String::from("abc你好");
s.truncate(2);
println!("{}", s);
}
fn main() {
let mut s = String::from("abc你好");
s.truncate(6);
println!("{}", s);
}
string.pop()
移除字符串末尾的字符并返回该字符
fn main() {
let mut s = String::from("abc你好");
let a = s.pop().unwrap();
println!("{}", s);
println!("{}", a);
}
string.remove(i)
移除索引i处的字符并返回该字符(i需要时字符边界)
fn main() {
let mut s = String::from("abc你好");
let a = s.remove(3);
// let a = s.remove(4); // 错误
println!("{}", s); // abc好
println!("{}", a); // 你
}
string.drain(range)
抽取文本内容并丢弃, range也是字符边界
fn main() {
let mut s = String::from("中国你好呀!");
s.drain(3..9); // 抽取 国你
println!("{}", s); // 中好呀!
}
string.replace_range(range, slice)
范围替换文本内容, range也是字符边界
fn main() {
let mut s = String::from("中国你好呀!");
s.replace_range(3..9, "aacc");
println!("{}", s); // 中aacc好呀!
}
搜索
为什么是&str,而不是String?
| 原因 | 说明 |
|---|---|
| 1. 零拷贝(Zero-copy) | &str 是只读视图,不拥有数据,无需复制或分配内存 |
| 2. 通用性(Generality) | &str 可以表示 String、字符串字面量、子串等 |
| 3. 性能(Performance) | 避免不必要的堆分配和数据拷贝 |
| 4. 所有权语义(Ownership) | 不需要转移所有权,函数可以接受任何字符串来源 |
| 5. 与 UTF-8 安全模型一致 | &str 保证是合法 UTF-8,而操作基于字节索引需边界检查 |
查找
slice.find(slice)
左到右
fn main() {
let s = "hello abab world nihao abab aacc";
let a = s.find("abab");
println!("{:?}", a); // Some(6)
}
slice.rfind(slice)
右到左
fn main() {
let s = "hello abab world nihao abab aacc";
let a = s.rfind("abab");
println!("{:?}", a); // Some(23)
}
替换
slice.replace(pattern, replacement)
返回新的 String,它是通过用 replacement 急性替换 pattern 的所有匹配项而形成的
fn main() {
let s = "hello abab world nihao abab aacc";
let a = s.replace("abab", "你好");
println!("s: {}", s); // hello 你好 world nihao 你好 aacc
println!("a: {}", a); // hello abab world nihao abab aacc
}
slice.replacen(pattern, replacement, n)
与replace一样,多了替换次数n
判断开头和结尾
slice.starts_with(pattern)
fn main() {
let s = "aaa.text";
let a = s.starts_with("aaa");
println!("a: {}", a); // a: true
}
slice.ends_with(pattern)
fn main() {
let s = "aaa.text";
let a = s.ends_with("text");
println!("a: {}", a); // a: true
}
遍历文本
slice.bytes()
按字节遍历
fn main() {
let s = "ab 你好 as12wde asd lk ";
let s1:Vec<_> = s.bytes().collect();
println!("s1: {:?}", s1); // s1: [97, 98, 32, 228, 189, 160, 229, 165, 189, 32, 32, 32, 97, 115, 49, 50, 119, 100, 101, 32, 97, 115, 100, 32, 108, 107, 32]
}
slice.chars()
按字符遍历
fn main() {
let s = "ab 你好 as12wde asd lk ";
let s1:Vec<_> = s.chars().collect();
println!("s1: {:?}", s1); // ['a', 'b', ' ', '你', '好', ' ', ' ', ' ', 'a', 's', '1', '2', 'w', 'd', 'e', ' ', 'a', 's', 'd', ' ', 'l', 'k', ' ']
}
分割
slice.split(pattern)
按内容pattern分割
如果输入以分隔符结尾, 保留末尾的空字符串
- slice.rsplit(pattern)
fn main() {
let s = "ab 你好 as12wde asd lk ";
let s1:Vec<&str> = s.split(' ').collect();
println!("s1: {:?}", s1); // s1: ["ab", "你好", "as12wde", "asd", "lk", ""]
}
slice.split_terminator(pattern)
忽略末尾的分隔符
- slice.rsplit_terminator(pattern)
fn main() {
let s = "ab 你好 as12wde asd lk ";
let s1:Vec<&str> = s.split(' ').collect();
println!("s1: {:?}", s1); // s1: ["ab", "你好", "as12wde", "asd", "lk", ""]
let s2:Vec<&str> = s.split_terminator(' ').collect();
println!("s2: {:?}", s2); // s2: ["ab", "你好", "as12wde", "asd", "lk"]
}
slice.split_whitespace()
按空白字符拆分
- slice.split_ascii_whitespace() 按ascii空白字符拆分
fn main() {
let s = "ab 你好 as12wde asd lk ";
let s1:Vec<&str> = s.split(' ').collect();
println!("s1: {:?}", s1);
let s2: Vec<&str> = s.split_whitespace().collect();
println!("s2: {:?}", s2);
}
slice.splitn(n, pattern)
分割n个
- slice.rsplitn(n, pattern)
fn main() {
let s = "ab 你好 as12wde asd lk ";
let s1:Vec<&str> = s.splitn(2, ' ').collect();
println!("s1: {:?}", s1);
}
slice.lines()
按行分割, “\n"换行 或 "\r\n”回车
fn main() {
let s = "ab 你好\nas12wde asd\nlk ";
let s1:Vec<&str> = s.lines().collect();
println!("s1: {:?}", s1);
}
slice.matches(pattern)
返回仅匹配的子字符串
- slice.rmatches(pattern)
fn main() {
let s = "asd coj sad salknd asd as";
let n: Vec<_> = s.matches("asd").collect();
println!("n: {:?}", n); // n: ["asd", "asd"]
}
slice.match_indices(pattern)
返回匹配项的(起始索引, 子字符串) 元组
- slice.rmatch_indices(pattern)
fn main() {
let s = "asd coj sad salknd asd as";
let n: Vec<_> = s.match_indices("asd").collect();
println!("n: {:?}", n); // n: [(0, "asd"), (19, "asd")]
}
修剪
slice.trim()
去掉首尾空白字符
- slice.trim_start() 除去开头空白字符
- slice.trim_end() 除去结尾空白字符
fn main() {
let s = " asd coj sad salknd asd as ";
let n = s.trim();
println!("s: {}", s); // asd coj sad salknd asd as
println!("n: {}", n); // asd coj sad salknd asd as
}
slice.trim_matches(pattern)
去掉首尾匹配项,重复移除(贪婪模式)
pattern支持char和字符数组/切片
- slice.trim_start_matches(pattern)
- slice.trim_end_matches(pattern)
pattern是char
只移除指定开头和尾部匹配项的字符
fn main() {
let s = "aaabbbhello worlfbaba";
let n = s.trim_matches('a');
println!("s: {}", s); // s: aaabbbhello worldbaba
println!("n: {}", n); // n: bbbhello worldbab
}
pattern是字符数组/切片
只移除指定多个字符开头和尾部匹配项的字符(匹配不到就停止)
fn main() {
let s = "aaabbbhello worldbaba";
let n = s.trim_matches(['a', 'b']);
println!("s: {}", s); // s: aaabbbhello worldbaba
println!("n: {}", n); // n: hello world
}
slice.strip_prefix(pattern)
移除固定前缀
返回一个 Some,其中携带了移除匹配文本之后的切片。否则,它会返回 None。
fn main() {
let s = "http://www.aaa.com";
let n = s.strip_prefix("http://");
println!("s: {}", s); // s: http://www.aaa.com
println!("n: {}", n); // n: www.aaa.com
}
slice.strip_suffix(pattern)
移除固定后缀
返回一个 Some,其中携带了移除匹配文本之后的切片。否则,它会返回 None。
fn main() {
let s = "aa.txt";
let n = s.strip_suffix(".txt").unwrap();
println!("s: {}", s); // s: aa.txt
println!("n: {}", n); // n: aa
}
字符串的大小写转换
slice.to_uppercase() 方法和 slice.to_lowercase() 方法会返回一个新分配的字符串,其中包含已转为大写或小写的 slice 文本。
fn main() {
let s = "aa.txt";
let n1 = s.to_uppercase();
let n2 = &n1.to_lowercase();
println!("s: {}", s); // s: aa.txt
println!("n1: {}", n1); // n1: AA.TXT
println!("n2: {}", n2); // n2: aa.txt
}
字符串转换类型
字符串中解析出其他类型
FromStr 是 Rust 标准库中的一个 trait(特征),它的核心作用就是:
将字符串(&str)安全地解析(parse)为其他类型的值。
pub trait FromStr {
type Err; // 解析失败时返回的错误类型
fn from_str(s: &str) -> Result<Self, Self::Err>;
}
use std::str::FromStr;
//
assert_eq!(usize::from_str("3628800"), Ok(3628800));
//
assert_eq!(f64::from_str("128.5625"), Ok(128.5625));
//
assert_eq!(bool::from_str("true"), Ok(true));
//
assert!(f64::from_str("not a float at all").is_err());
// 布尔值
assert!(bool::from_str("TRUE").is_err());
// char 类型也实现了 FromStr,用于解析只有一个字符的字符串
assert_eq!(char::from_str("é"), Ok('é'));
自定义FromStr
use std::str::FromStr;
#[derive(Debug)]
enum Color {
Red, Green, Blue,
}
impl FromStr for Color {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"red" => Ok(Color::Red),
"green" => Ok(Color::Green),
"blue" => Ok(Color::Blue),
_ => Err(format!("Unknown color: {}", s)),
}
}
}
fn main() {
let c = Color::from_str("RED").unwrap();
println!("{:?}", c); // Red
}
parse() 与 FromStr 的关系
str 类型上的 .parse::
() 方法 要求 T: FromStr
它本质上是 FromStr::from_str(self) 的语法糖
"123".parse::<i32>()
// 等价于
i32::from_str("123")
其它类型转为字符串
使用format!宏
实现std::fmt::Display特型
let s = format!("{}", 22);
Rust 对字符串处理的核心哲学:UTF-8 是唯一、默认且强制的文本编码。
Rust 的 str/String 底层是 UTF-8 字节,操作时需注意字节 vs 字符
let s = "你好";
println!("{}", s.len()); // 输出 6(字节长度!不是字符数)
// 因为 "你" = 3 字节,"好" = 3 字节,UTF-8 编码
只有合法 UTF-8 字节才能转为 String,否则会报错
let bytes = vec![0xe4, 0xbd, 0xa0, 0xe5, 0xa5, 0xbd]; // "你好" 的 UTF-8 字节
let s = String::from_utf8(bytes).unwrap(); // ✅ 合法 UTF-8 → 文本
println!("{}", s); // 你好
str::from_utf8(byte_slice)
- ✅ 不分配新内存:返回的 &str 直接指向 bytes 的内存。
- ⚠️ 生命周期受限:&str 的生命周期不能超过 bytes。
- 📌 适用于:只读、临时解析,比如处理网络包、文件映射等。
use std::str;
let bytes = b"Hello, 世界"; // &[u8] 字面量
match str::from_utf8(bytes) {
Ok(s) => println!("Valid UTF-8: {}", s), // s: &str
Err(e) => println!("Invalid UTF-8: {}", e),
}
String::from_utf8(vec)
拥有,转移所有权
- 零拷贝转换:Rust 内部直接把 Vec
的堆缓冲区“重解释”为 String,不复制字节! - 获得所有权:返回的 String 可以长期使用、移动、修改。
- 适用于:从 I/O 读取、加密解密、协议解析等场景,你已经拥有了字节数据,想把它变成字符串。
let bytes = vec![0x48, 0x65, 0x6c, 0x6c, 0x6f]; // Vec<u8>,拥有数据
match String::from_utf8(bytes) {
Ok(s) => println!("Got string: {}", s), // s: String(拥有数据)
Err(e) => println!("Invalid UTF-8: {:?}", e.into_bytes()), // 可取回原始 Vec
}
String::from_utf8_lossy(byte_slice) -> Cow
有损转换:非法字节 → 替代
如果遇到非法 UTF-8 字节序列,不会 panic 或报错,而是用 Unicode 替换字符(U+FFFD)代替。
let bytes = b"Hello \xFF\xFE World"; // 包含非法字节
let s = String::from_utf8_lossy(bytes);
println!("{}", s); // 输出: Hello �� World
String::from_utf8_unchecked(vec)
跳过检查!我保证它是 UTF-8
let s = String::from("Hello");
let bytes = s.into_bytes(); // 合法 UTF-8 字节
// 安全!因为 bytes 来自合法 String
let s2 = unsafe { String::from_utf8_unchecked(bytes) };
str::from_utf8_unchecked(vec)
跳过检查!我保证它是 UTF-8(只读版)
fn parse_known_utf8(data: &[u8]) -> &str {
// 假设上层已验证 data 是 UTF-8
unsafe { std::str::from_utf8_unchecked(data) }
}
区别
| 函数 | 安全性 | 输入 | 输出 | 是否验证 UTF-8 | 适用场景 |
|---|---|---|---|---|---|
String::from_utf8_lossy |
✅ 安全 | &[u8] |
Cow<str> |
❌ 不验证,非法→� |
处理不可信输入 |
String::from_utf8_unchecked |
⚠️ unsafe |
Vec<u8> |
String |
❌ 跳过验证 | 已知合法 + 高性能 |
str::from_utf8_unchecked |
⚠️ unsafe |
&[u8] |
&str |
❌ 跳过验证 | 已知合法 + 零拷贝 |
延迟分配Cow
- 如果数据不需要修改 → 用 Cow::Borrowed(&T)(零拷贝)
- 如果数据需要修改 → 用 Cow::Owned(T)(拥有所有权)
use std::borrow::Cow;
fn need_modify(name: &str) -> bool {
if name.to_uppercase() == name {
true
} else {
false
}
}
fn process(name: &str) -> Cow<'_, str> {
if need_modify(name) {
Cow::Owned(name.to_uppercase()) // 需要时才分配
} else {
Cow::Borrowed(name) // 直接借用,零成本
}
}
fn main() {
// 使用示例
let a = process("hello"); // Cow::Borrowed("hello") → 无分配
let b = process("WORLD"); // Cow::Owned("world") → 有分配
// 统一使用
println!("{}", a); // 都能打印
println!("{}", b);
}

浙公网安备 33010602011771号