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

devtools/2025/3/5 12:04:26/

Rust语言圣经中定义

str

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

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

rust">let string: str = "banana";

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

rust">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 离开作用域时,其占用的内存会被自动释放
使用场景:当需要对字符串进行修改(如追加、删除字符)时使用

rust">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 和字面量字符串)

rust">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 类型的变量,而函数参数要求引用时使用

rust">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 数据,并且希望拥有其所有权时使用

rust">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 是借用类型
使用场景:在处理与操作系统交互的字符串时使用,如文件系统操作

rust">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 风格字符串时使用

rust">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

rust">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

rust">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 或字节切片来获取长度

rust">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()

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

&OsString转为Option<&str>

rust">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 引用

rust">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 会自动进行解引用转换。

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类型的变量时,也会发生自动解引用转换。

rust">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干嘛

rust">pub fn as_os_str(&self) -> &OsStr

显式表达意图

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

rust">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.ppmy.cn/devtools/164737.html

相关文章

2025-03-04 学习记录--C/C++-C语言 判断是否是素数

合抱之木&#xff0c;生于毫末&#xff1b;九层之台&#xff0c;起于累土&#xff1b;千里之行&#xff0c;始于足下。&#x1f4aa;&#x1f3fb; C语言 判断是否是素数 一、代码 ⭐️ #include <stdio.h> #include <stdbool.h> // 使用 bool 类型// 判断是否是…

Versal - XRT(CPP) 2024.1

目录 1.简介 2. XRT 2.1 XRT vs OpenCL 2.2 Takeways 2.3 XRT C APIs 2.4 Device and XCLBIN 2.5 Buffers 2.5.1 Buffer 创建 2.5.1.1 普通 Buffer 2.5.1.2 特殊 Buffer 2.5.1.3 用户指针 Buffer 2.5.2 Data Transfer 2.5.2.1 read/write API 2.5.2.2 map API 2…

请你说一下你对服务降级的理解

服务降级是指在服务出现故障或资源不足的情况下&#xff0c;通过牺牲部分功能或性能&#xff0c;以确保核心功能或服务的可用性。其主要目的是在系统面临压力或故障时&#xff0c;仍然能够提供基本的服务&#xff0c;而不是完全失败。 服务降级的核心思想 保证核心功能&#x…

RMSNorm模块

目录 代码代码解释1. 初始化方法 __init__2. 前向传播方法 forward3. 总结4. 使用场景 可视化 代码 class RMSNorm(torch.nn.Module):def __init__(self, dim: int, eps: float):super().__init__()self.eps epsself.weight nn.Parameter(torch.ones(dim))def forward(self,…

合理规划时间,从容应对水利水电安全员考试

合理规划时间&#xff0c;从容应对水利水电安全员考试 在忙碌的工作与生活节奏中备考水利水电安全员考试&#xff0c;合理规划时间是实现高效备考的核心。科学的时间管理能让你充分利用每一分每一秒&#xff0c;稳步迈向考试成功。 制定详细的学习计划是第一步。依据考试时间…

二、Redis 安装与基本配置:全平台安装指南 服务器配置详解

Redis 安装与基本配置:全平台安装指南 & 服务器配置详解 Redis 作为高性能的内存数据库,其安装和配置是使用 Redis 的第一步。本篇文章将全面介绍 Redis 的安装方式,覆盖 Windows、Linux、Docker 环境,并详细讲解 Redis 的基础配置,包括 持久化、日志、端口设置等。此…

USRP B205mini 开箱— 沉浸式组装

USRP B205mini 开箱— 沉浸式组装 开箱 USRP b205mini&#xff1a;初体验 刚刚收到了心心念念的USRP b205mini&#xff0c;这是我在无线电爱好路上的新伙伴。开箱这一刻&#xff0c;正如解锁未知世界的一把钥匙。拆开快递&#xff0c;轻巧的包装盒映入眼帘&#xff0c;简约大气…

【零基础C语言】第四节 数组

【零基础C语言系列】 【零基础C语言】第一节 C语言概述【数制进制码制】-CSDN博客 【零基础C语言】第二节 数据类型、运算符、表达式-CSDN博客 【零基础C语言】第三节 控制结构-CSDN博客 一、一维数组