news 2026/6/24 13:35:16

【Rust】字符串(String 与 str) 详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【Rust】字符串(String 与 str) 详解

Rust 提供两种主要字符串类型:String(可变,拥有所有权)和&str(不可变,借用)。

1.字符串基础

创建字符串

// 多种创建方式lets1=String::new();// 空字符串lets2="初始内容".to_string();// &str 转 Stringlets3=String::from("Hello");// from 方法lets4="字面量".to_owned();// to_owned(克隆)// 字符串字面量(&'static str)letliteral:&str="我是字面量";

更新字符串

letmuts=String::new();// 追加字符s.push('H');// "H"s.push_str("ello");// "Hello"// 拼接lets1=String::from("Hello, ");lets2=String::from("World!");lets3=s1+&s2;// s1被消耗,s3 = "Hello, World!"// 格式拼接lets4=format!("{}{}","Hi, ","Rust!");// "Hi, Rust!"

2.字符串操作

访问内容

lets=String::from("你好,世界");// 按字节访问(UTF-8编码)forbyteins.bytes(){println!("{}",byte);// UTF-8字节序列}// 按字符访问(Unicode标量值)forchins.chars(){println!("{}",ch);// '你', '好', ',', '世', '界'}// 字符迭代器方法letchar_count=s.chars().count();// 字符数:5letbyte_len=s.len();// 字节数:15(中文3字节/字符)

切片操作

lets=String::from("Hello, World!");// 创建字符串切片lethello=&s[0..5];// "Hello"letworld=&s[7..12];// "World"// 小心!必须按字符边界切片// let bad = &s[0..1]; // 对多字节字符会panic!// 安全切片方法ifs.is_char_boundary(3){letslice=&s[0..3];// 安全切片}

3.字符串方法

查询与检查

lets=String::from("Hello Rust");// 长度信息letbyte_len=s.len();// 字节长度letis_empty=s.is_empty();// 是否为空// 包含检查lethas_hello=s.contains("Hello");// trueletstarts=s.starts_with("He");// trueletends=s.ends_with("Rust");// true// 查找letfind_idx=s.find("Rust");// Some(6)letrfind_idx=s.rfind('l');// Some(3)(从右向左)

修改操作

letmuts=String::from("Hello World");// 替换lets1=s.replace("World","Rust");// 新字符串:"Hello Rust"s.replace_range(6..11,"Rust");// 原地替换:"Hello Rust"// 删除s.remove(5);// 移除索引5的字符(',')s.pop();// 移除最后一个字符s.truncate(5);// 截断到前5个字符s.clear();// 清空字符串// 插入s.insert(5,',');// 在索引5插入字符s.insert_str(0,"Prefix ");// 插入字符串

4.字符串转换

类型转换

// String ↔ &strletstring=String::from("hello");letstr_slice:&str=&string;// 自动解引用letowned:String=str_slice.to_string();// 数字转字符串letnum_str=42.to_string();// "42"letfloat_str=format!("{:.2}",3.14159);// "3.14"// 字符串转数字letnum:i32="42".parse().unwrap();// 42letnum_result:Result<i32,_>="42".parse();// Ok(42)

字符编码

// UTF-8 编码/解码lets="🦀 Rust";// 包含emoji// 获取UTF-8字节letbytes=s.as_bytes();// &[u8]// 从字节创建(需要有效UTF-8)letfrom_bytes=String::from_utf8(bytes.to_vec());// 处理无效UTF-8letlossy=String::from_utf8_lossy(b"Hello\xFFWorld");// "Hello�World"(替换无效字节)

5.字符串格式化

format! 宏

// 基本格式化lets=format!("{} + {} = {}",1,2,3);// "1 + 2 = 3"// 控制格式letpi=format!("PI: {:.3}",3.14159);// "PI: 3.142"lethex=format!("0x{:X}",255);// "0xFF"letbin=format!("{:b}",10);// "1010"// 位置参数lets=format!("{1} {0}","World","Hello");// "Hello World"// 命名参数lets=format!("{name} is {age}",name="Alice",age=30);

其他格式化宏

print!("Hello");// 打印不换行println!("World");// 打印换行eprint!("Error");// 标准错误打印eprintln!("Error with newline");lets=format!("Debug: {:?}",vec![1,2,3]);// 调试格式lets=format!("Pretty: {:#?}",vec![1,2,3]);// 美化调试

6.字符串性能优化

预分配容量

// 避免重复分配letmuts=String::with_capacity(100);// 预分配100字节s.push_str("Hello");s.push_str(" World");println!("长度: {}, 容量: {}",s.len(),s.capacity());// 长度: 11, 容量: 100(无需重新分配)

字符串重用

// 复用String缓冲区letmuts=String::new();foriin0..10{s.clear();// 清空内容,保留容量s.push_str(&format!("Item {}",i));// 处理s...}

使用Cow(写时复制)

usestd::borrow::Cow;fnprocess(input:&str)->Cow<str>{ifinput.contains("bad"){Cow::Owned(input.replace("bad","good"))// 需要时克隆}else{Cow::Borrowed(input)// 直接借用}}letresult=process("hello");// Cow::Borrowed("hello")letresult2=process("bad word");// Cow::Owned("good word")

7.常用字符串模式

字符串分割

lets="apple,banana,orange";// 按分隔符分割letfruits:Vec<&str>=s.split(',').collect();// ["apple", "banana", "orange"]// 保留分隔符letparts:Vec<&str>=s.split_inclusive(',').collect();// ["apple,", "banana,", "orange"]// 多分隔符lets2="apple, banana; orange";letfruits2:Vec<&str>=s2.split([',',';',' ']).collect();// 行分割lettext="line1\nline2\r\nline3";letlines:Vec<&str>=text.lines().collect();

字符串修剪

lets=" Hello Rust! \n";lettrimmed=s.trim();// "Hello Rust!"(移除两端空白)letleft_trimmed=s.trim_start();// "Hello Rust! \n"letright_trimmed=s.trim_end();// " Hello Rust!"// 自定义修剪字符lets2="***Hello***";lettrimmed2=s2.trim_matches('*');// "Hello"

大小写转换

lets="Hello Rust";letupper=s.to_uppercase();// "HELLO RUST"letlower=s.to_lowercase();// "hello rust"// 首字母大写fncapitalize(s:&str)->String{letmutchars=s.chars();matchchars.next(){None=>String::new(),Some(first)=>first.to_uppercase().chain(chars).collect(),}}letcap=capitalize("hello");// "Hello"

8.字符串与集合

字符串向量

// 字符串集合letwords=vec!["hello","world","rust"].into_iter().map(|s|s.to_string()).collect::<Vec<String>>();// 连接字符串letsentence=words.join(" ");// "hello world rust"// 拼接多个字符串letconcatenated=["foo","bar","baz"].concat();// "foobarbaz"

字符处理

lets="Hello 世界";// 字符统计letchar_count=s.chars().count();// 7letgrapheme_count=s.chars().count();// 注意:可能不是字形簇数// 字符位置ifletSome(pos)=s.char_indices().find(|(_,ch)|*ch=='世').map(|(i,_)|i){println!("'世'在位置 {}",pos);// 输出位置}

9.字符串与I/O

文件读写

usestd::fs;usestd::io::{self,Write,BufRead};// 读取文件为字符串letcontent=fs::read_to_string("file.txt")?;// 写入字符串到文件fs::write("output.txt","Hello World")?;// 逐行读取letfile=fs::File::open("file.txt")?;letreader=io::BufReader::new(file);forlineinreader.lines(){letline=line?;println!("{}",line);}

用户输入

usestd::io;letmutinput=String::new();println!("请输入:");io::stdin().read_line(&mutinput)// 读取一行.expect("读取失败");lettrimmed=input.trim();// 移除换行符println!("你输入了: {}",trimmed);

10.字符串最佳实践

选择指南

  1. 函数参数:优先使用&str而非&String
  2. 返回类型:需要所有权时用String,否则考虑Cow<str>
  3. 性能敏感:预分配容量,避免重复分配
  4. 国际文本:使用chars()而非字节索引

常见错误避免

// ❌ 错误:多字节字符切片// let s = "你好";// let slice = &s[0..1]; // panic!// ✅ 正确:使用字符迭代lets="你好";ifletSome(first_char)=s.chars().next(){println!("首字符: {}",first_char);}// ❌ 避免:不必要的String分配fnbad_greeting(name:&String)->String{format!("Hello, {}",name)// 可以接受&str}// ✅ 改进:使用&str参数fngood_greeting(name:&str)->String{format!("Hello, {}",name)}

String vs &str 总结

特性String&str
所有权拥有数据借用数据
可变性可变不可变
内存位置栈、堆或静态内存
大小动态编译时已知
性能有分配开销无分配开销

核心原则

  • 需要修改或拥有字符串时使用String
  • 只读引用时使用&str
  • 函数参数优先接受&str以兼容两者
  • 使用to_string()into()进行必要转换

Rust的字符串设计确保了内存安全和UTF-8编码正确性,虽然增加了复杂性,但提供了强大的安全保证。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/24 10:33:54

小白指南:QTimer::singleShot常见误区避免

延时执行不翻车&#xff1a;深入理解QTimer::singleShot的正确打开方式你有没有遇到过这样的情况&#xff1f;写了一行看似简单的代码&#xff1a;QTimer::singleShot(1000, []{ qDebug() << "Hello"; });结果——什么都没发生。或者更糟&#xff0c;程序在几秒…

作者头像 李华
网站建设 2026/6/21 12:15:54

RS232接口引脚定义常见错误:工业环境避坑指南

工业串口通信避坑实录&#xff1a;RS232引脚定义与系统稳定性实战指南在自动化车间的深夜&#xff0c;一台PLC突然失去响应&#xff0c;HMI屏幕闪烁着“通信中断”警告。工程师赶到现场&#xff0c;重启设备、更换线缆、调整波特率……折腾两小时后才发现&#xff0c;问题竟出在…

作者头像 李华
网站建设 2026/6/22 8:48:58

LangFlow Webhook功能接入第三方通知服务

LangFlow Webhook功能接入第三方通知服务 在企业智能化转型的浪潮中&#xff0c;一个常见的痛点浮现出来&#xff1a;AI模型虽然能准确理解用户意图、完成复杂推理&#xff0c;但它的“行动力”却往往停留在输出文本上。比如客服系统中的大模型识别出用户投诉订单未发货&#x…

作者头像 李华
网站建设 2026/6/18 17:19:03

青少年编程竞赛辅导:题目解析与算法思路启发

青少年编程竞赛辅导&#xff1a;题目解析与算法思路启发 在信息学奥赛的备赛过程中&#xff0c;学生常常面临一个看似简单却异常棘手的问题&#xff1a;面对海量真题和零散笔记&#xff0c;如何快速找到某道经典题目的解法思路&#xff1f;比如&#xff0c;“最大子段和”有没有…

作者头像 李华
网站建设 2026/6/21 12:50:49

仅限内部人员知晓的Open-AutoGLM测试版Web地址泄露(速看)

第一章&#xff1a;Open-AutoGLM测试版泄露事件概述近期&#xff0c;开源社区广泛关注的大型语言模型项目 Open-AutoGLM 被曝出其未发布的测试版本在非官方渠道泄露。该事件引发了关于模型安全、开发流程合规性以及潜在滥用风险的广泛讨论。据初步调查&#xff0c;泄露版本包含…

作者头像 李华
网站建设 2026/6/15 14:49:33

display driver uninstaller 在游戏本驱动重装中的深度剖析

用 DDU 彻底重装显卡驱动&#xff1a;游戏本性能回归的终极秘籍你有没有遇到过这样的情况&#xff1f;明明刚更新了最新的 NVIDIA 驱动&#xff0c;结果《赛博朋克2077》一进城市就帧数暴跌&#xff1b;或者外接显示器死活检测不到&#xff0c;设备管理器里还跳出个未知设备&am…

作者头像 李华