Rust~String、str、str、String、Box<str> 或 Box<str>

Rust语言圣经中定义

str

Rust 语言类型大致分为两种:基本类型和标准库类型,前者由语言特性直接提供,后者在标准库中定义

str 是唯一定义在 Rust 语言特性中的字符串,但也是几乎不会用到的字符串类型
str 字符串是 DST 动态大小类型,编译器无法在编译期知道 str 类型的大小,只有到了运行期才能动态获知
这对于强类型、强安全的 Rust 语言来说不可接受

let string: str = "banana";

创建一个 str 类型的字符串,编译会报错:

error[E0277]: the size for values of type `str` cannot be known at compilation time--> src/main.rs:4:9|
4 |     let string: str = "banana";|         ^^^^^^ doesn't have a size known at compile-time

原因:
所有的切片都是动态类型,无法直接被使用,str 是字符串切片,[u8] 是数组切片
str 是 String 和 &str 的底层数据类型

String

str 类型是硬编码进可执行文件,无法被修改
String 是一个可增长、可改变且具有所有权的 UTF-8 编码的字符串
Rust 提到字符串时,往往指的是 String 类型和 &str 字符串切片类型,这两个类型都是 UTF-8 编码

示例

在这里插入图片描述
在这里插入图片描述

其他

Rust 的标准库还提供了其他类型的字符串,例如 OsString, OsStr, CsString 和 CsStr 等
这些名字都以 String 或者 Str 结尾,它们分别对应的是具有所有权和被借用的变量

具体区分

String

类型本质:String 是一个可变的、拥有所有权的字符串类型,存储在堆上,并且可以动态增长
内存管理:由 Rust 的所有权系统管理,当 String 离开作用域时,其占用的内存会被自动释放
使用场景:当需要对字符串进行修改(如追加、删除字符)时使用

fn main() {let mut s = String::from("hello");s.push_str(", world!");println!("{}", s);
}

str

类型本质:str 是一个不可变的、无固定大小的字符串切片类型,通常被称为 “字符串切片”。没有所有权,只是对存储在其他地方的字符串数据的引用
内存管理:由于 str 是无固定大小的类型,不能直接使用,通常以引用的形式 &str 出现
使用场景:用于表示一个字符串的一部分,如字符串的子串

&str

类型本质:&str 是对 str 类型的引用,是一个不可变的字符串切片。它指向一个连续的 UTF - 8 编码的字节序列
内存管理:是一个借用类型,不拥有底层数据的所有权,只要借用的对象存在,&str 就有效
使用场景:当只需要读取字符串内容而不需要修改它时,使用 &str 作为函数参数可以接受多种字符串类型(如 String 和字面量字符串)

fn print_string(s: &str) {println!("{}", s);
}fn main() {let s1 = String::from("hello");print_string(&s1);print_string("world");
}

&String

类型本质:&String 是对 String 类型的引用
内存管理:是一个借用类型,不拥有 String 的所有权,只要 String 对象存在,&String 就有效
使用场景:通常在已经有 String 类型的变量,而函数参数要求引用时使用

fn print_string_ref(s: &String) {println!("{}", s);
}fn main() {let s = String::from("hello");print_string_ref(&s);
}

Box<str>

类型本质:Box<str> 是将 str 类型装箱到堆上的类型。它拥有底层 str 的所有权
内存管理:当 Box<str> 离开作用域时,底层的 str 数据会被自动释放
使用场景:当需要在堆上存储 str 数据,并且希望拥有其所有权时使用

fn main() {let s: Box<str> = "hello".into();println!("{}", s);
}

Box<&str>

类型本质:Box<&str> 不是一个常见用法,&str 本身是引用类型,将其装箱没意义。Box<&str> 会在堆上存储一个 &str 引用
内存管理:Box<&str> 拥有这个引用的所有权,但引用指向的数据本身的生命周期需要单独管理
使用场景:一般不建议使用,容易造成混淆

OsString 和 OsStr

类型本质:OsString 是一个拥有所有权的字符串类型,用于表示操作系统相关的字符串,如文件路径。OsStr 是 OsString 的不可变视图,类似于 str 是 String 的不可变视图
内存管理:OsString 管理自己的内存,OsStr 是借用类型
使用场景:在处理与操作系统交互的字符串时使用,如文件系统操作

use std::ffi::OsString;
use std::path::PathBuf;fn main() {let os_string = OsString::from("example.txt");let path = PathBuf::from(os_string);println!("{:?}", path);
}

注意:OsString 和 String 的内存管理不一样

CsString 和 CsStr

类型本质:CsString 和 CsStr 是 cstr_core crate 中的类型,用于处理以空字符结尾的 C 风格字符串。CsString 拥有字符串数据,CsStr 是不可变视图
内存管理:CsString 管理自己的内存,CsStr 是借用类型
使用场景:在与 C 代码进行交互时,需要处理 C 风格字符串时使用

use cstr_core::CStr;
use std::ffi::CString;fn main() {let c_string = CString::new("hello").unwrap();let c_str = c_string.as_c_str();println!("{:?}", c_str);
}

注意:CsString 和 String 的内存管理不一样

如何查看字符串的大小

String

fn main() {// String 内部是以 UTF - 8 字节序列存储// String 是 Rust 标准库中用于表示可增长、拥有所有权的 UTF - 8 编码字符串的类型let s = String::from("Hello, 世界");// 获取字节长度let byte_length = s.len();// 13println!("Byte length: {}", byte_length);// 获取字符数量let char_count = s.chars().count();// 9println!("Character count: {}", char_count);
}

CsString

use cstr_core::CString;fn main() {let cs_string = CString::new("Hello, C-style").expect("Failed to create CString");// 获取不包含空字符的字节长度let byte_length_without_nul = cs_string.as_c_str().to_bytes().len();// 14println!("Byte length without null terminator: {}", byte_length_without_nul);// 获取包含空字符的字节长度let byte_length_with_nul = cs_string.as_c_str().to_bytes_with_nul().len();// 15println!("Byte length with null terminator: {}", byte_length_with_nul);
}

OsString

可以将 OsString 转换为 OsStr,然后根据操作系统的编码方式将其转换为 &str 或字节切片来获取长度

use std::ffi::OsString;fn main() {let os_string = OsString::from("Hello, OS");// 尝试将 OsString 转换为 &str 并获取长度if let Some(s) = os_string.to_str() {let byte_length = s.len();// 9println!("Byte length: {}", byte_length);}
}

在上述代码中,os_string.to_str() 尝试将 OsString 转换为 &str,如果转换成功,则可以使用 len() 方法获取其字节长度。需要注意的是,to_str() 可能会失败,因为 OsString 可能包含非 UTF - 8 编码的数据。如果需要处理非 UTF - 8 数据,可以使用 os_string.into_vec() 方法将其转换为字节向量,然后获取向量的长度。
综上所述,不同类型的字符串获取长度的方法有所不同,需要根据具体类型和需求选择合适的方法。

std::ffi::OsString的os_string.to_str()

pub fn to_str(&self) -> Option<&str>

&OsString转为Option<&str>

pub trait Deref {type Target: ?Sized;// Required methodfn deref(&self) -> &Self::Target;
}// 
impl ops::Deref for OsString {type Target = OsStr;#[inline]fn deref(&self) -> &OsStr {&self[..]}
}

to_str() 是 OsStr 类型的一个方法,OsString 可以通过自动解引用转换为 OsStr 来调用这个方法。
to_str() 方法的作用是尝试将 OsStr 转换为 &str
它的实现原理是检查 OsStr 内部存储的字节序列是否是有效的 UTF - 8 编码。如果是有效的 UTF - 8 编码,就返回一个 Some(&str);如果不是有效的 UTF - 8 编码,则返回 None

Deref 机制主要用于在需要某个类型的引用时,自动将一个类型的引用转换为另一个类型的引用。
String 实现了 Deref<Target = str>,意味着当有一个 &String 类型的变量时,Rust 会自动将其转换为 &str,以便可以调用 str 类型的方法。

os_string.to_str() 并不是依赖 Deref 来完成从 OsString 到 &str 的转换。它是通过检查 OsStr 内部字节序列的 UTF - 8 有效性来进行转换的。

自动解引用

以 OsString 转换 OsStr 为例
在 Rust 中,OsString 可以自动解引用转换为 OsStr,这种转换主要发生在以下几种场景:

调用 OsStr 方法时

当调用一个 OsStr 类型的方法,而实际操作的是 OsString 实例时,Rust 会自动进行解引用转换。
这是因为 OsString 实现了 Deref<Target = OsStr> 特征,该特征允许 Rust 在需要 OsStr 引用的地方使用 OsString 引用

use std::ffi::OsString;fn main() {let os_string = OsString::from("example.txt");// 调用 OsStr 的 to_str 方法,这里自动将 OsString 转换为 OsStrif let Some(s) = os_string.to_str() {println!("Converted to &str: {}", s);}
}

在上述代码中,os_string 是 OsString 类型,但 to_str 是 OsStr 类型的方法

作为函数参数传递时

当一个函数的参数类型是 &OsStr,而传递的是 &OsString 时,Rust 会自动进行解引用转换。

use std::ffi::{OsStr, OsString};fn print_os_str(os_str: &OsStr) {if let Some(s) = os_str.to_str() {println!("{}", s);}
}fn main() {let os_string = OsString::from("test.txt");// 自动将 &OsString 转换为 &OsStr 传递给函数print_os_str(&os_string);
}

赋值给 &OsStr 类型变量时

当将一个 &OsString 赋值给一个 &OsStrc类型的变量时,也会发生自动解引用转换。

use std::ffi::OsString;fn main() {let os_string = OsString::from("file.txt");// 自动将 &OsString 转换为 &OsStrlet os_str: &OsStr = &os_string;if let Some(s) = os_str.to_str() {println!("{}", s);}
}

这里,&os_string&OsString 类型,而 os_str 是 &OsStr 类型,Rust 会自动完成转换。

既然可以通过Defef自动转换,那还要as_os_str干嘛

pub fn as_os_str(&self) -> &OsStr

显式表达意图

代码的可读性和可维护性在软件开发中至关重要。使用 as_os_str 方法可以更清晰地表达想要将 OsString 转换为 &OsStr 的意图。相比自动解引用,显式调用方法能让阅读代码的人一眼就明白正在进行类型转换操作。

use std::ffi::OsString;fn main() {let os_string = OsString::from("example.txt");// 显式转换,意图清晰let os_str = os_string.as_os_str(); if let Some(s) = os_str.to_str() {println!("{}", s);}
}

避免潜在的混淆

在某些复杂的代码场景中,自动解引用可能会导致代码的行为变得难以理解。自动解引用是 Rust 编译器在背后自动完成的,当代码中有多个类型实现了 Deref 特征时,可能会引发混淆。使用 as_os_str 可以避免这种潜在的混淆,让代码的行为更加明确。

与其他类型转换保持一致性

在 Rust 标准库中,很多类型都提供了显式的类型转换方法,比如 String 有 as_str 方法用于转换为 &strVec<T> 有 as_slice 方法用于转换为 &[T]。OsString 的 as_os_str 方法与这些设计保持一致,使得代码的风格更加统一。

代码审查和调试

在代码审查过程中,显式的类型转换方法更容易被审查人员识别和理解。同时,在调试代码时,显式调用方法可以让调试者更清楚地看到类型转换的位置和过程,有助于快速定位问题。

总结

尽管 Deref 自动转换提供了便利,但 as_os_str 方法通过显式表达意图、避免混淆、保持一致性以及方便代码审查和调试等方面,为代码的质量和可维护性提供了保障。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/896806.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

大数据SQL调优专题——底层调优

引入 上一篇我们提到了调优的常见切入点&#xff0c;核心就是通过数据产出情况发现问题&#xff0c;借助监控等手段收集信息排查瓶颈在哪&#xff0c;最后结合业务理解&#xff0c;等价重写思路去解决问题。 在实际工作场景中&#xff0c;去保证数据链路产出SLA的时候&#x…

Hue 编译异常:ImportError: cannot import name ‘six‘ from ‘urllib3.packages‘

个人博客地址&#xff1a;Hue 编译异常&#xff1a;ImportError: cannot import name six from urllib3.packages | 一张假钞的真实世界 在编译Hue的时候出现错误信息如下&#xff1a; Running /home/zhangjc/ysten/git/ysten-hue/build/env/bin/hue makemigrations --noinpu…

计算机网络——详解TCP三握四挥

文章目录 前言一、三次握手1.1 三次握手流程1.2 tcp为什么需要三次握手建立连接&#xff1f; 二、四次挥手2.1 四次挥手流程2.2 为什么是四次&#xff0c;不是三次&#xff1f;2.3 为什么要等待2msl&#xff1f;2.4 TCP的保活计时器 前言 TCP和UDP是计算机网络结构中运输层的两…

# C# 中堆(Heap)与栈(Stack)的区别

在 C# 中&#xff0c;堆和栈是两种不同的内存分配机制&#xff0c;它们在存储位置、生命周期、性能和用途上存在显著差异。理解堆和栈的区别对于优化代码性能和内存管理至关重要。 1. 栈&#xff08;Stack&#xff09; 1.1 定义 栈是一种后进先出&#xff08;LIFO&#xff0…

如何把图片或者图片地址存到 MySQL 数据库中以及如何将这些图片数据通过 JSP 显示在网页中

如何优雅地管理图片&#xff1a;从MySQL数据库存储到JSP展示的全流程解析 在互联网时代&#xff0c;一张引人入胜的图片往往能为网站带来巨大的流量。而作为开发者的我们&#xff0c;如何高效地管理和展示这些图片资源则成为了一项重要的技术挑战。今天&#xff0c;我们就一起…

「拼好帧」小黄鸭 Lossless Scaling 软件介绍与下载

「拼好帧」小黄鸭 Lossless Scaling 软件介绍与下载 在游戏和视频播放时&#xff0c;你是否遇到过分辨率不匹配、画质模糊的问题&#xff1f;今天给大家介绍一款神器——Lossless Scaling&#xff08;拼好帧&#xff09;&#xff0c;也被玩家们亲切地称为“小黄鸭”&#xff0…

科普|无人机专业术语

文章目录 前言一、飞控二、电调三、通道四、2S、3S、4S电池五、电池后面C是什么意思?六、电机的型号七、什么是电机的KV值?八、螺旋桨的型号九、电机与螺旋桨的搭配 前言 无人机飞控系统控制飞行姿态&#xff0c;电调控制电机转速&#xff0c;遥控器通道控制飞行动作。电池C…

和鲸科技携手四川气象,以 AI 的力量赋能四川气象一体化平台建设

气象领域与农业、能源、交通、环境科学等国计民生关键领域紧密相连&#xff0c;发挥着不可替代的重要作用。人工智能技术的迅猛发展&#xff0c;为气象领域突破困境带来了新的契机。AI 技术能够深度挖掘气象大数据中蕴含的复杂信息&#xff0c;助力人类更精准地把握自然规律&am…

Linux mount命令

Linux mount命令是经常会使用到的命令&#xff0c;它用于挂载Linux系统外的文件。 一、挂载功能介绍 挂载方法&#xff1a;mount DECE MOUNT_POINT 命令使用格式&#xff1a;mount [-fnrsvw] [-t vfstype] [-o options] device dir device&#xff1a;指明要挂载的设备&…

《Operating System Concepts》阅读笔记:p177-p178

《Operating System Concepts》学习第 18 天&#xff0c;p177-p178 总结&#xff0c;总计 2 页。 一、技术总结 1.implicit thread A programming model that transfers the creation and management of threading from application developers to compilers and run-time l…

Redis缓存一致性难题:如何让数据库和缓存不“打架”?

标题&#xff1a;Redis缓存一致性难题&#xff1a;如何让数据库和缓存不“打架”&#xff1f;&#xff08;附程序员脱发指南&#xff09; 导言&#xff1a;当数据库和缓存成了“异地恋” 想象一下&#xff1a;你刚在美团下单了一份麻辣小龙虾&#xff0c;付款后刷新页面&#…

委托者模式(掌握设计模式的核心之一)

目录 问题&#xff1a; 举例&#xff1a; 总结&#xff1a;核心就是利用Java中的多态来完成注入。 问题&#xff1a; 今天刷面经&#xff0c;刷到装饰者模式&#xff0c;又进阶的发现委托者模式&#xff0c;发现还是不理解&#xff0c;特此记录。 举例&#xff1a; ​老板​…

[密码学实战]Java实现SM4加解密(ecb,cbc)及工具验证

前言 在现代信息安全领域,数据加密技术是保障数据安全的核心手段之一。SM4作为中国国家密码管理局发布的对称加密算法,因其高效性和安全性,广泛应用于金融、政务、通信等领域。本文将详细介绍如何使用Java实现SM4的加解密操作,并深入探讨SM4的几种常见加密模式及其原理。 …

动态规划刷题

文章目录 动态规划三步问题题目解析代码 动态规划 1. 状态表示&#xff1a;dp[i]&#xff0c;表示dp表中i下标位置的值 2. 状态转移方程&#xff1a;以i位置位置的状态&#xff0c;最近的一步来划分问题&#xff0c;比如可以将状态拆分成前状态来表示现状态&#xff0c;dp[i] …

Vue 3 搭建前端模板并集成 Ant Design Vue(2025)

目录 一、环境安装 二、创建项目 三、前端工程化配置 四、引入组件库 五、选择 API 风格 1、选项式 API (Options API)​ 2、组合式 API (Composition API)​ 六、页面信息修改 七、通用布局选择 1、基础布局结构 2、全局底部栏 3、动态替换内容 4、全局顶部栏 …

C++杂记——RTTI

run-time type information or run-time type identification (RTTI) RTTI&#xff08;Runtime Type Information&#xff09;是C中的一个特性&#xff0c;允许程序在运行时获取类型信息。它主要用于多态&#xff08;尤其是基于类的多态&#xff09;时&#xff0c;帮助判断对象…

【Mac】git使用再学习

目录 前言 如何使用github建立自己的代码库 第一步&#xff1a;建立本地git与远程github的联系 生成密钥 将密钥加入github 第二步&#xff1a;创建github仓库并clone到本地 第三步&#xff1a;上传文件 常见的git命令 git commit git branch git merge/git rebase …

六十天前端强化训练之第七天CSS预处理器(Sass)案例:变量与嵌套系统详解

欢迎来到编程星辰海的博客讲解 目录 一、知识讲解&#xff08;3000字&#xff09; 1. Sass基础概念 2. 变量系统 2.1 变量定义 2.2 数据类型 2.3 作用域优先级 2.4 变量实践场景 3. 嵌套系统 3.1 选择器嵌套 3.2 属性嵌套 3.3 嵌套规则 二、核心代码示例 完整SCSS…

Docker和K8S中pod、services、container的介绍和关系

在容器化技术中&#xff0c;Docker、Kubernetes&#xff08;K8S&#xff09;、Pod、Service 和 Container 是核心概念&#xff0c;理解它们的关系对构建和管理现代应用至关重要。以下是详细的分步解释&#xff1a; 1. 核心概念定义 (1) Container&#xff08;容器&#xff09;…

DeepSeek掘金——DeepSeek R1驱动的PDF机器人

DeepSeek掘金——DeepSeek R1驱动的PDF机器人 本指南将引导你使用DeepSeek R1 + RAG构建一个功能性的PDF聊天机器人。逐步学习如何增强AI检索能力,并创建一个能够高效处理和响应文档查询的智能聊天机器人。 本指南将引导你使用DeepSeek R1 + RAG构建一个功能性的PDF聊天机器人…