主页 > 人工智能  > 

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

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(); // 13 println!("Byte length: {}", byte_length); // 获取字符数量 let char_count = s.chars().count(); // 9 println!("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(); // 14 println!("Byte length without null terminator: {}", byte_length_without_nul); // 获取包含空字符的字节长度 let byte_length_with_nul = cs_string.as_c_str().to_bytes_with_nul().len(); // 15 println!("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(); // 9 println!("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 method fn 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 转换为 OsStr if 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 转换为 &OsStr let 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 方法用于转换为 &str,Vec<T> 有 as_slice 方法用于转换为 &[T]。OsString 的 as_os_str 方法与这些设计保持一致,使得代码的风格更加统一。

代码审查和调试

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

总结

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

标签:

Rust~String、str、str、String、Box<str>或Box<str>由讯客互联人工智能栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“Rust~String、str、str、String、Box<str>或Box<str>