Rust 编程指南·酷色篇 #05 - 颜色名字与字符串

本章目标:掌握Rust字符串操作,为颜色系统添加命名和文本处理能力
字符串处理,让颜色有名字的艺术
想象一下,如果世界上所有的颜色都只能用数字表示,那该多么枯燥!就像我们给宠物起名字一样,给颜色起个好听的名字,不仅方便记忆,更能表达情感和意义。
不同语言的字符串对比:
- JavaScript: 只有一种字符串类型,简单但功能有限
- Java: String不可变,StringBuilder可变,概念清晰但使用复杂
- Rust: String可变,&str不可变,性能优异且内存安全
1. 用户痛点:颜色需要有意义的名字
想象一下这个场景:你保存了很多颜色,但都是 #FF6B35、rgb(255, 107, 53) 这样的数字。几天后你想找"那个温暖的橙色",却要在一堆数字中翻找。

痛苦的现状:
fn main() {
println!("=== 痛苦的现状:数字颜色 ===");
let color1 = (255, 107, 53); // 这是什么颜色?
let color2 = (46, 134, 171); // 这又是什么?
let color3 = (162, 59, 114); // 完全记不住!
println!("颜色1: {:?}", color1);
println!("颜色2: {:?}", color2);
println!("颜色3: {:?}", color3);
println!("几天后你还记得哪个是\"温暖的橙色\"吗?");
}
我们需要什么:
- 给颜色起有意义的名字
- 通过名字快速找到颜色
- 支持中文、英文等多种命名
- 能够修改和管理颜色名称
2. Rust字符串基础:两种字符串类型
什么是字符串?
字符串就像给颜色贴的标签。想象你有一盒颜料,每个颜料管上都有标签写着颜色名称。在编程中,字符串就是这些标签,帮我们用文字描述和识别数据。
Rust中有两种主要字符串类型,就像颜料和调色板的关系:
为什么需要两种字符串类型?
- 有时我们需要固定不变的标签(比如商品标签)
- 有时我们需要可以修改的标签(比如可擦写的白板)
- Rust通过两种类型来满足不同需求,既保证安全又提高性能
graph TD
A[字符串数据] --> B[String类型<br/>可修改的调色板<br/>拥有颜料]
A --> C[&str类型<br/>只读的标签<br/>指向颜料]
B --> D[可以添加、修改颜色名]
C --> E[只能查看,不能修改]
String vs &str 对比:
fn main() {
println!("\n=== String vs &str:两种字符串类型 ===");
// String类型 - 像可以添加颜料的调色板
let mut color_name = String::from("温暖橙");
color_name.push_str("色"); // 可以修改
println!("String类型(可修改): {}", color_name);
// &str类型 - 像固定的标签
let fixed_label = "经典蓝"; // 不能修改
println!("&str类型(只读): {}", fixed_label);
// 类型转换
let from_str = "活力红".to_string(); // &str -> String
let to_str = &color_name; // String -> &str
println!("转换: '活力红' -> {}", from_str);
println!("借用: {}", to_str);
}
关键理解:
String:拥有数据,可以修改,像你的私人调色板- 可以增加内容(push_str)
- 可以修改内容
- 占用堆内存,性能稍慢但灵活
&str:借用数据,只读访问,像商店里的颜色样本- 不能修改内容
- 通常指向程序中的固定文本
- 性能更快,内存占用小
生活比喻:
String= 可以随时添加贴纸的笔记本&str= 印刷好的书页,内容固定不变
3. 颜色命名系统:让颜色有名字
解决方案思路:
我们要把"颜色名字"和"RGB数值"组合在一起,就像给每个颜料管贴上标签。在Rust中,我们可以用元组来实现这个组合。
什么是元组?
元组就像一个小盒子,可以装不同类型的东西。比如 (String, (u8, u8, u8)) 就是一个装着"名字"和"RGB值"的盒子。
现在我们来解决开头的痛点,用简单的方式给颜色起名字:

fn main() {
println!("\n=== 简单颜色命名:元组组合 ===");
// 用元组组合颜色名称和RGB值
let sunset_orange = ("日落橙".to_string(), (255, 107, 53));
let ocean_blue = ("海洋蓝".to_string(), (46, 134, 171));
let rose_pink = ("玫瑰粉".to_string(), (162, 59, 114));
// 显示命名颜色
println!("我的颜色收藏:");
display_named_color(&sunset_orange);
display_named_color(&ocean_blue);
display_named_color(&rose_pink);
}
// 显示命名颜色的函数
fn display_named_color(color: &(String, (u8, u8, u8))) {
let (name, (r, g, b)) = color;
println!("\x1b[38;2;{};{};{}m● {}\x1b[0m", r, g, b, name);
}
更进一步:颜色重命名:

fn rename_color(color: &mut (String, (u8, u8, u8)), new_name: &str) {
color.0 = new_name.to_string();
println!("颜色已重命名为: {}", color.0);
}
fn main() {
let mut my_color = ("旧名字".to_string(), (255, 107, 53));
println!("重命名前:");
display_named_color(&my_color);
// 重命名颜色
rename_color(&mut my_color, "温暖橙");
println!("重命名后:");
display_named_color(&my_color);
}
代码解释:
- 用元组
(String, (u8, u8, u8))组合名称和RGB值- 第一个元素:颜色名字(String类型,可修改)
- 第二个元素:RGB值(三个u8数字的元组)
to_string()创建可修改的字符串- 把字符串字面量转换为String类型
- 这样就可以后续修改名字了
- 函数参数使用
&str接受字符串字面量- 函数不需要拥有字符串,只需要读取
- 使用借用更高效,避免不必要的复制
重要概念:
- 元组解构:
let (name, (r, g, b)) = color;把元组拆开使用 - 借用:
&符号表示借用,不获取所有权 - 可变借用:
&mut表示可以修改借用的数据
4. 字符串操作实战:颜色名称处理
为什么需要字符串操作?
在实际使用中,我们经常需要:
- 组合多个词语形成颜色名(如:“深” + “海蓝”)
- 根据用户输入动态生成名称
- 清理和格式化用户输入的名字
字符串拼接的三种方式:
每种方式都有不同的使用场景,就像不同的工具适合不同的工作:

fn color_name_operations() {
// 方法1: + 运算符 (消耗左侧所有权)
let base = "深".to_string();
let full_name = base + "海蓝"; // base不能再使用
// 方法2: format! 宏 (推荐使用)
let color_type = "暖";
let color_base = "橙";
let formatted_name = format!("{}色{}", color_type, color_base);
// 方法3: push_str 方法 (直接修改)
let mut growing_name = String::from("天空");
growing_name.push_str("蓝");
println!("拼接结果1: {}", full_name);
println!("拼接结果2: {}", formatted_name);
println!("拼接结果3: {}", growing_name);
// 使用建议
println!("\n=== 使用建议 ===");
println!("+ 运算符:简单拼接,但会消耗左侧变量");
println!("format! 宏:复杂格式化,最灵活(推荐)");
println!("push_str:直接修改,性能最好");
}
颜色名称验证和处理:
为什么需要验证?
用户输入的名字可能有各种问题:
- 前后有空格:" 红色 "
- 完全为空:“”
- 大小写不统一:“red” vs “Red”
我们需要一个函数来"清洗"这些输入,让它们变得规范:

fn process_color_name(name: &str) -> String {
// 去除空白字符(前后的空格、制表符等)
let trimmed = name.trim();
// 检查是否为空
if trimmed.is_empty() {
return "未命名颜色".to_string();
}
// 首字母大写处理
let mut chars: Vec<char> = trimmed.chars().collect(); // 转为字符数组
if let Some(first_char) = chars.get_mut(0) { // 获取第一个字符
*first_char = first_char.to_uppercase().next().unwrap_or(*first_char);
}
chars.into_iter().collect() // 重新组合成字符串
}
// 更简单的版本(适合初学者)
fn simple_process_name(name: &str) -> String {
let clean_name = name.trim(); // 去空格
if clean_name.is_empty() {
"未命名颜色".to_string()
} else {
clean_name.to_string()
}
}
fn main() {
let names = vec![" 红色 ", "", "blue", "绿"];
println!("=== 颜色名称处理 ===");
for name in names {
let processed = process_color_name(name);
println!("'{}' -> '{}'", name, processed);
}
}
5. 颜色搜索系统:通过名字找颜色
搜索的重要性:
当你的颜色库越来越大时,快速找到想要的颜色就变得很重要。就像在图书馆找书一样,我们需要不同的搜索方式:
- 精确搜索:知道确切名字时使用
- 模糊搜索:只记得部分名字时使用
Rust中的搜索方法:
find()- 找到第一个匹配的项filter()- 找到所有匹配的项Option类型 - 处理可能找不到的情况

// 通过名字查找颜色(精确匹配)
fn find_color_by_name(colors: &Vec<(String, (u8, u8, u8))>, name: &str) -> Option<&(String, (u8, u8, u8))> {
// iter() 创建迭代器遍历所有颜色
// find() 找到第一个满足条件的项
// |color| color.0 == name 是闭包,检查颜色名是否匹配
colors.iter().find(|color| color.0 == name)
}
// 模糊搜索颜色(包含关键词)
fn search_colors(colors: &Vec<(String, (u8, u8, u8))>, keyword: &str) -> Vec<&(String, (u8, u8, u8))> {
// filter() 筛选出所有满足条件的项
// contains() 检查字符串是否包含关键词
// collect() 把迭代器结果收集成Vec
colors.iter().filter(|color| color.0.contains(keyword)).collect()
}
// 不区分大小写的搜索(更实用)
fn search_colors_ignore_case(colors: &Vec<(String, (u8, u8, u8))>, keyword: &str) -> Vec<&(String, (u8, u8, u8))> {
let keyword_lower = keyword.to_lowercase();
colors.iter().filter(|color| color.0.to_lowercase().contains(&keyword_lower)).collect()
}
fn main() {
// 创建颜色集合
let mut my_colors = vec![
("日落橙".to_string(), (255, 107, 53)),
("海洋蓝".to_string(), (46, 134, 171)),
("森林绿".to_string(), (34, 139, 34)),
("玫瑰红".to_string(), (220, 20, 60)),
];
// 显示所有颜色
println!("=== 我的颜色库 ({} 种颜色) ===", my_colors.len());
for (index, color) in my_colors.iter().enumerate() {
print!("{}: ", index + 1);
display_named_color(color);
}
// 精确搜索
println!("\n=== 搜索 '海洋蓝' ===");
if let Some(color) = find_color_by_name(&my_colors, "海洋蓝") {
display_named_color(color);
} else {
println!("未找到该颜色");
}
// 模糊搜索
println!("\n=== 搜索包含 '红' 的颜色 ===");
let red_colors = search_colors(&my_colors, "红");
for color in red_colors {
display_named_color(color);
}
}
6. 实用功能:颜色名称生成器
自动命名的价值:
有时候我们有RGB值但不知道叫什么名字,这时候自动生成器就很有用了。虽然生成的名字可能不够诗意,但至少能让我们快速识别颜色类型。
算法思路:
- 比较RGB三个分量的大小
- 找出占主导地位的颜色
- 考虑混合色的情况(如黄色=红+绿)
- 返回对应的颜色名称

fn generate_color_name(rgb: (u8, u8, u8)) -> String {
let (r, g, b) = rgb;
// 判断主要颜色分量
let main_color = if r > g && r > b {
"红色" // 红色分量最大
} else if g > r && g > b {
"绿色" // 绿色分量最大
} else if b > r && b > g {
"蓝色" // 蓝色分量最大
} else if r > 200 && g > 200 {
"黄色" // 红+绿 = 黄色(光学混合)
} else if g > 200 && b > 200 {
"青色" // 绿+蓝 = 青色
} else if r > 200 && b > 200 {
"紫色" // 红+蓝 = 紫色
} else {
"灰色" // 其他情况默认为灰色
};
main_color.to_string()
}
// 更详细的颜色分析器
fn analyze_color_name(rgb: (u8, u8, u8)) -> String {
let (r, g, b) = rgb;
let total = r as u16 + g as u16 + b as u16;
// 判断亮度
let brightness = if total > 600 { "浅" } else if total > 300 { "" } else { "深" };
// 判断主色调
let hue = if r > g && r > b { "红" }
else if g > r && g > b { "绿" }
else if b > r && b > g { "蓝" }
else { "灰" };
format!("{}{}", brightness, hue)
}
fn main() {
let test_colors = vec![
(255, 0, 0), // 纯红
(0, 255, 0), // 纯绿
(0, 0, 255), // 纯蓝
(255, 255, 0), // 黄色
(128, 128, 128), // 灰色
];
println!("=== 自动颜色命名 ===");
for rgb in test_colors {
let name = generate_color_name(rgb);
let named_color = (name, rgb);
display_named_color(&named_color);
}
}
7. 字符串格式化:美化颜色显示
格式化的重要性:
好的显示格式能让信息更容易阅读和理解。就像商店里的商品标签,清晰的格式能帮助用户快速获取需要的信息。
Rust格式化语法:
{}- 基本占位符{:02X}- 十六进制,至少2位,不足补0{:^15}- 居中对齐,总宽度15{:3}- 右对齐,宽度3
ANSI颜色代码:
\x1b[38;2;r;g;bm- 设置前景色为RGB\x1b[0m- 重置所有格式

fn format_color_info(color: &(String, (u8, u8, u8))) -> String {
let (name, (r, g, b)) = color;
// 多种格式化方式
format!(
"颜色名称: {}\n\
RGB值: ({}, {}, {})\n\
十六进制: #{:02X}{:02X}{:02X}",
name, r, g, b, r, g, b
)
}
fn create_color_card(color: &(String, (u8, u8, u8))) -> String {
let (name, (r, g, b)) = color;
format!(
"\x1b[38;2;{};{};{}m┌─────────────────────┐\n\
│\t{:^15}\t \n\
│\tRGB: {:3},{:3},{:3}\t \n\
│\t#{:02X}{:02X}{:02X}\t\t \n\
└─────────────────────┘\x1b[0m",
r, g, b,
name,
r, g, b,
r, g, b
)
}
fn main() {
let sunset = ("日落橙".to_string(), (255, 107, 53));
println!("=== 详细信息 ===");
println!("{}", format_color_info(&sunset));
println!("\n=== 颜色卡片 ===");
println!("{}", create_color_card(&sunset));
}
8. 综合演示:完整的颜色命名系统
系统整合思路:
现在我们要把前面学到的所有功能组合起来,创建一个真正实用的颜色管理系统。这个系统将包括:
- 颜色库管理(添加、删除、修改)
- 智能搜索功能
- 自动命名生成
- 美观的显示格式
- 用户交互界面
现在让我们把所有功能整合起来,构建一个完整的颜色命名系统:

fn comprehensive_demo() {
println!("\n=== 🎨 完整颜色命名系统演示 ===");
// 1. 创建颜色库(使用Vec存储多个颜色)
let mut color_library = vec![
("温暖橙".to_string(), (255, 107, 53)),
("深海蓝".to_string(), (46, 134, 171)),
("森林绿".to_string(), (34, 139, 34)),
];
println!("1. 当前颜色库 ({} 种颜色):", color_library.len());
for (i, color) in color_library.iter().enumerate() {
print!(" {}: ", i + 1); // 显示序号
display_named_color(color);
}
// 2. 添加新颜色(演示动态添加)
println!("\n2. 添加新颜色:");
let new_color = ("玫瑰粉".to_string(), (255, 182, 193));
color_library.push(new_color);
println!(" 已添加: ");
display_named_color(color_library.last().unwrap());
// 3. 搜索功能演示
println!("\n3. 搜索功能演示:");
let search_keyword = "蓝";
println!(" 搜索关键词: '{}'", search_keyword);
let results = search_colors(&color_library, search_keyword);
if results.is_empty() {
println!(" 未找到匹配的颜色");
} else {
println!(" 找到 {} 个匹配结果:", results.len());
for result in results {
print!(" ");
display_named_color(result);
}
}
// 4. 自动命名演示
println!("\n4. 自动颜色命名:");
let mystery_colors = vec![(255, 0, 0), (0, 255, 255), (128, 0, 128)];
for rgb in mystery_colors {
let auto_name = generate_color_name(rgb);
let named_color = (auto_name, rgb);
print!(" 自动识别: ");
display_named_color(&named_color);
}
// 5. 颜色重命名演示
println!("\n5. 颜色重命名:");
if let Some(color) = color_library.get_mut(0) {
println!(" 重命名前: {}", color.0);
color.0 = "夕阳橙".to_string();
print!(" 重命名后: ");
display_named_color(color);
}
// 6. 最终颜色库状态
println!("\n6. 最终颜色库 ({} 种颜色):", color_library.len());
for (i, color) in color_library.iter().enumerate() {
print!(" {}: ", i + 1);
display_named_color(color);
}
}
fn main() {
// 运行所有演示
comprehensive_demo();
println!("\n=== 🎉 字符串与颜色命名学习完成! ===");
println!("你已经掌握了:");
println!("✅ String 和 &str 的区别和使用");
println!("✅ 字符串拼接的多种方法");
println!("✅ 颜色命名和搜索系统");
println!("✅ 字符串格式化和美化显示");
println!("✅ 综合运用构建完整系统");
}!(" {}: ", i + 1);
display_named_color(color);
}
// 2. 添加自动命名的颜色
println!("\n2. 添加自动命名的颜色:");
let new_rgb = (220, 20, 60);
let auto_name = generate_color_name(new_rgb);
let new_color = (auto_name, new_rgb);
color_library.push(new_color);
print!(" 新增: ");
display_named_color(color_library.last().unwrap());
// 3. 搜索演示
println!("\n3. 搜索演示:");
println!(" 包含'绿'的颜色:");
for color in &color_library {
if color.0.contains("绿") {
print!(" - ");
display_named_color(color);
}
}
// 4. 格式化显示
println!("\n4. 详细信息展示:");
if let Some(color) = color_library.first() {
println!("{}", format_color_info(color));
}
println!("\n✨ 现在颜色不再是冰冷的数字,而是有温度的名字!");
}
fn main() {
comprehensive_demo();
}
9. 本章总结
核心知识点:
-
String vs &str:
String:可修改,拥有所有权,适合存储&str:只读,借用引用,适合参数传递
-
字符串操作:
- 拼接:
+、format!、push_str - 处理:
trim、contains、replace - 转换:
to_string、&string
- 拼接:
-
实战应用:
- 颜色命名系统
- 搜索和查找功能
- 自动名称生成
- 格式化显示
价值总结:
- 用户友好:数字变成有意义的名字
- 易于管理:通过名字快速找到颜色
- 支持搜索:模糊匹配和精确查找
- 自动化:智能生成颜色名称
学习建议:
- 理解 String 和 &str 的使用场景
- 多练习字符串的各种操作方法
- 在实际项目中应用颜色命名系统
下一章预告:颜色的条件判断
第7章将学习 if/else 条件判断,让程序能够根据颜色特性做出智能决策。
你将学会:
- 根据亮度自动选择文字颜色
- 颜色分类和智能判断
- 布尔逻辑在颜色中的应用
- 构建智能的颜色助手
恭喜完成字符串处理的学习!现在你的颜色不再是冰冷的数字,而是有温度的名字!
更多文章和视频知识资讯,大家可以关注我的公众号、掘金和 B 站 。让我们一起成长,变得更强。我们下次再见~
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)