- VisualStudio2022插件的安装及使用-编程手把手系列文章
- pprof-在现网场景怎么用
- C#实现的下拉多选框,下拉多选树,多级节点
- 【学习笔记】基础数据结构:猫树
字符串是每种编程语言都绕不开的类型, 。
不过,在Rust中,你会看到远比其他语言更加丰富多样的字符串类型.
如下图:
为什么Rust中需要这么多种表示字符串的类型呢?
初学Rust时,可能无法理解为什么要这样设计?为什么要给使用字符串带来这么多不必要的复杂性?
其实,Rust中对于字符串的设计,优先考虑的是安全,高效和灵活, 。
所以在易用性方面,感觉没有其他语言(比如python,golang)那么易于理解和掌握.
本文尝试解释Rust中的所有不同的字符串类型,以及它们各自的特点.
希望能让大家更好的理解Rust为了安全和发挥最大性能的同时,是如何处理字符串的.
我们代码中的字符串或者数字,存储在机器中,都是二进制,也就是0和1组成的序列.
程序将二进制数据转换为人类可读的字符串 需要两个关键信息:
常见的编码有ASCII,UTF-8等等,编码就是二进制序列对应的字符, 。
比如,ASCII是8位二进制对应一个字符,所以它最多只能表示256种不同的字符.
而UTF-8可以使用8位~32位二进制来表示一个字符,这意味着它可以编码超过一百万个字符, 。
包括世界上的每种语言和各种表情符号等复杂字符.
通过字符编码,我们可以将二进制和字符互相转换, 。
再通过字符串长度信息,我们将内存中的二进制转换为字符串时,就能知道何时停止.
Rust中的字符串,统一采用UTF-8编码,下面一一介绍各种字符串类型及其使用场景.
String和&str是Rust中使用最多的两种字符串类型,也是在使用中容易混淆的两种类型.
String是分配在堆上的,可增长的UTF-8字符串, 。
它拥有底层的数据,并且在超出其定义的范围被自动清理释放.
let my_string = String::from("databook");
println!(
"pointer: {:p}, length: {}, capacity: {}",
&my_string,
my_string.len(),
my_string.capacity()
);
对于一个String,主要部分有3个:
Pointer
:指向堆内存中字符串的起始位置Length
:有效字符串的长度Capacity
:字符串my_string
总共占用的空间注意这里Length和Capacity的区别,Length是my_string中有效字符的长度,也就是字符串实际的长度; 。
Capacity表示系统为my_string分配的内存空间,一般来说,Capacity >= Length.
通常不需要直接处理Capacity,但它的存在对于编写高效且资源敏感的Rust代码时很重要.
特别是,当你知道即将向String添加大量内容时,可能会事先手动保留足够的Capacity以避免多次内存重新分配.
&str则是一个字符串的切片,它表示一个连续的字符序列, 。
它是一个借用类型,并不拥有字符串数据,只包含指向切片开头的指针和切片长度.
let my_str: &str = "databook";
println!("pointer: {:p}, length: {}", &my_str, my_str.len());
注意,&str没有Capacity方法,因为它只是一个借用,内容不可能增加.
最后,对于String和&str,使用时建议:
String
&str
这两种形式是将字符串表示位字节的形式,其中Vec[u8]是字节向量,&[u8]是字节切片.
它们只是将字符串中的各个字符转换成字节形式.
as_bytes方法可将&str转换为&[u8]; 。
into_bytes方法可将String转换为Vec<u8>.
let my_str: &str = "databook";
let my_string = String::from("databook");
let s: &[u8] = my_str.as_bytes();
let ss: Vec<u8> = my_string.into_bytes();
println!("s: {:?}", s);
println!("ss: {:?}", ss);
/* 运行结果
s: [100, 97, 116, 97, 98, 111, 111, 107]
ss: [100, 97, 116, 97, 98, 111, 111, 107]
*/
在UTF-8编码中,每个英文字母对应1个字节,而一个中文汉字对应3个字节.
let my_str: &str = "中文";
let my_string = String::from("中文");
let s: &[u8] = my_str.as_bytes();
let ss: Vec<u8> = my_string.into_bytes();
println!("s: {:?}", s);
println!("ss: {:?}", ss);
/* 运行结果
s: [228, 184, 173, 230, 150, 135]
ss: [228, 184, 173, 230, 150, 135]
*/
Vec[u8]和&[u8]以字节的形式存储字符串,不用关心字符串的具体编码, 。
这在网络中传输二进制文件或者数据包时非常有用,可以有效每次传输多少个字节.
str类型本身是不能直接使用的,因为它的大小在编译期无法确定,不符合Rust的安全规则.
但是,它可以与其他具有特殊用途的指针类型一起使用.
如果需要一个字符串切片的所有权(&str是借用的,没有所有权),那么可以使用Box智能指针.
当你想要冻结字符串以防止进一步修改或通过删除额外容量来节省内存时,它非常有用.
比如,下面的代码,我们将一个String转换为Box<str>, 。
这样,可以确保它不会在其他地方被修改,也可以删除它,因为Box<str>拥有字符串的所有权.
let my_string = String::from("databook");
let my_box_str = my_string.into_boxed_str();
println!("{}", my_box_str);
// 这一步会报错,因为所有权已经转移
// 这是 Box<str> 和 &str 的区别
// println!("{}", my_string);
当你想要在多个地方共享一个不可变的字符串的所有权,但是又不克隆实际的字符串数据时, 。
可以尝试使用Rc<str>智能指针.
比如,我们有一个非常大的文本,想在多个地方使用,又不想复制多份占用内存,可以用Rc<str>.
let my_str: &str = "very long text ....";
let rc_str1: Rc<str> = Rc::from(my_str);
let rc_str2 = Rc::clone(&rc_str1);
let rc_str3 = Rc::clone(&rc_str1);
println!("rc_str1: {}", rc_str1);
println!("rc_str2: {}", rc_str2);
println!("rc_str3: {}", rc_str3);
/* 运行结果
rc_str1: very long text ....
rc_str2: very long text ....
rc_str3: very long text ....
*/
这样,在不实际克隆字符串数据的情况下,让多个变量拥有其所有权.
Arc<str>与Rc<str>的功能类似,主要的区别在于Arc<str>是线程安全的.
如果在多线程环境下,请使用Arc<str>.
let my_str: &str = "very long text ....";
let arc_str: Arc<str> = Arc::from(my_str);
let mut threads = vec![];
let mut cnt = 0;
while cnt < 5 {
let s = Arc::clone(&arc_str);
let t = thread::spawn(move || {
println!("thread-{}: {}", cnt, s);
});
threads.push(t);
cnt += 1;
}
for t in threads {
t.join().unwrap();
}
/* 运行结果
thread-0: very long text ....
thread-3: very long text ....
thread-2: very long text ....
thread-1: very long text ....
thread-4: very long text ....
*/
上面的代码中,在5个线程中共享了字符串数据.
上面运行结果中,线程顺序是不固定的,多执行几遍会有不一样的顺序.
Cow是Copy-on-Write(写入时复制)的缩写, 。
当你需要实现一个功能,根据字符串的内容来决定是否需要修改它,使用Cow就很合适.
比如,过滤敏感词汇时,我们把敏感词汇替换成xx.
fn filter_words(input: &str) -> Cow<str> {
if input.contains("sb") {
let output = input.replace("sb", "xx");
return Cow::Owned(output);
}
Cow::Borrowed(input)
}
当输入字符串input中含有敏感词sb时,会重新分配内存,生成新字符串; 。
否则直接使用原字符串,提高内存效率.
CStr和CString是与C语言交互时用于处理字符串的两种类型.
CStr用于在Rust中安全地访问由C语言分配的字符串; 。
而CString用于在Rust中创建和管理可以安全传递给C语言函数的字符串.
C风格的字符串与Rust中的字符串实现方式不一样, 。
比如,C语言中的字符串都是以null字符\0结尾的字节数组,这点就与Rust很不一样.
所以Rust单独封装了这两种类型(CStr和CString),可以安全的与C语言进行字符串交互,从而实现与现有的C语言库和API无缝集成.
OsStr 和 OsString 是用于处理与操作系统兼容的字符串类型.
主要用于需要与操作系统API进行交互的场景,这些API一般特定于平台的字符串编码(比如Windows上的UTF-16,以及大多数Unix-like系统上的UTF-8).
OsStr 和OsString 也相当于str和String的关系,所以OsStr 一般不直接在代码中使用, 。
使用比较多的是&OsStr和OsString.
这两个类型一般用于读取/写入操作系统环境变量或者与系统API交互时,帮助我们确保字符串以正确的格式传递.
这两个类型看名字似乎和字符串关系不大,实际上它们是专门用来处理文件路径字符串的.
在不同的文件系统中,对于文件路径的格式,路径中允许使用的字符都不一样,比如,windows系统中文件路径甚至不区分大小写.
使用Path 和 PathBuf,我们编码时就不用分散精力去关心具体使用的是哪种文件系统.
Path和PathBuf的主要区别在于可变性和所有权, 。
如果需要频繁读取和查询路径信息而不修改它,Path是一个好选择; 。
如果需要动态构建或修改路径内容,PathBuf则更加合适.
总之,Rust中字符串类型之所以多,是因为根据不同的用途对字符串类型做了分类.
这也是为了处理不同的应用场景时让程序发挥最大的性能,毕竟,安全和高性能一直是Rust最大的卖点.
最后此篇关于Rust字符串类型全解析的文章就讲到这里了,如果你想了解更多关于Rust字符串类型全解析的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我正在尝试编写一个相当多态的库。我遇到了一种更容易表现出来却很难说出来的情况。它看起来有点像这样: {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE
谁能解释一下这个表达式是如何工作的? type = type || 'any'; 这是否意味着如果类型未定义则使用“任意”? 最佳答案 如果 type 为“falsy”(即 false,或 undef
我有一个界面,在IAnimal.fs中, namespace Kingdom type IAnimal = abstract member Eat : Food -> unit 以及另一个成功
这个问题在这里已经有了答案: 关闭 10 年前。 Possible Duplicate: What is the difference between (type)value and type(va
在 C# 中,default(Nullable) 之间有区别吗? (或 default(long?) )和 default(long) ? Long只是一个例子,它可以是任何其他struct类型。 最
假设我有一个案例类: case class Foo(num: Int, str: String, bool: Boolean) 现在我还有一个简单的包装器: sealed trait Wrapper[
这个问题在这里已经有了答案: Create C# delegate type with ref parameter at runtime (1 个回答) 关闭 2 年前。 为了即时创建委托(dele
我正在尝试获取图像的 dct。一开始我遇到了错误 The function/feature is not implemented (Odd-size DCT's are not implemented
我正在尝试使用 AFNetworking 的 AFPropertyListRequestOperation,但是当我尝试下载它时,出现错误 预期的内容类型{( “应用程序/x-plist” )}, 得
我在下面收到错误。我知道这段代码的意思,但我不知道界面应该是什么样子: Element implicitly has an 'any' type because index expression is
我尝试将 SignalType 从 ReactiveCocoa 扩展为自定义 ErrorType,代码如下所示 enum MyError: ErrorType { // .. cases }
我无法在任何其他问题中找到答案。假设我有一个抽象父类(super class) Abstract0,它有两个子类 Concrete1 和 Concrete1。我希望能够在 Abstract0 中定义类
我想知道为什么这个索引没有用在 RANGE 类型中,而是用在 INDEX 中: 索引: CREATE INDEX myindex ON orders(order_date); 查询: EXPLAIN
我正在使用 RxJava,现在我尝试通过提供 lambda 来订阅可观察对象: observableProvider.stringForKey(CURRENT_DELETED_ID) .sub
我已经尝试了几乎所有解决问题的方法,其中包括。为 提供类型使用app.use(express.static('public'))还有更多,但我似乎无法为此找到解决方案。 index.js : imp
以下哪个 CSS 选择器更快? input[type="submit"] { /* styles */ } 或 [type="submit"] { /* styles */ } 只是好
我不知道这个设置有什么问题,我在 IDEA 中获得了所有注释(@Controller、@Repository、@Service),它在行号左侧显示 bean,然后转到该 bean。 这是错误: 14-
我听从了建议 registering java function as a callback in C function并且可以使用“简单”类型(例如整数和字符串)进行回调,例如: jstring j
有一些 java 类,加载到 Oracle 数据库(版本 11g)和 pl/sql 函数包装器: create or replace function getDataFromJava( in_uLis
我已经从 David Walsh 的 css 动画回调中获取代码并将其修改为 TypeScript。但是,我收到一个错误,我不知道为什么: interface IBrowserPrefix { [
我是一名优秀的程序员,十分优秀!