为什么Rust有String和str?String和str之间有什么区别?什么时候使用String而不是str,反之亦然?其中一个被弃用了吗?
当前回答
在这三种不同类型中let面条=“面条”.to_string();let oodles=面条[1..];让贵宾犬=“ಠ_ಠ“;//这是字符串文本字符串有一个可调整大小的缓冲区,用于保存UTF-8文本。缓冲区是在堆上分配的,因此它可以根据需要调整缓冲区的大小,或者请求。在示例中,“面条”是一个字符串,它拥有八字节缓冲器,其中七个正在使用。你可以想到字符串作为Vec,保证保持格式良好的UTF-8;在里面事实上,这就是String的实现方式。&str是对其他人拥有的UTF-8文本的引用:它“借用”了文本。在示例中,oodles是一个&str参考属于“面条”的文本的最后六个字节,因此它表示文本“oodles”。与其他切片引用一样,&str是一个胖指针,包含实际数据的地址和其长度。你可以把&str看作是&[u8],保证保持格式良好的UTF-8。字符串文字是一个&str,它引用预先分配的文本,通常与程序的机器一起存储在只读存储器中密码在前面的示例中,贵宾犬是一个字符串文本,指向到程序开始执行时创建的七个字节,以及直到它退出。这就是它们在内存中的存储方式
参考资料:Jim Blandy、Jason Orendorff、Leonora F。S.廷德尔
其他回答
str,仅用作&str,是一个字符串片段,是对UTF-8字节数组的引用。
字符串过去是~str,一个可增长的、拥有的UTF-8字节数组。
一些用法
示例1.rs
fn main(){
let hello = String::("hello");
let any_char = hello[0];//error
}
示例_2.rs
fn main(){
let hello = String::("hello");
for c in hello.chars() {
println!("{}",c);
}
}
示例_3.rs
fn main(){
let hello = String::("String are cool");
let any_char = &hello[5..6]; // = let any_char: &str = &hello[5..6];
println!("{:?}",any_char);
}
阴影
fn main() {
let s: &str = "hello"; // &str
let s: String = s.to_uppercase(); // String
println!("{}", s) // HELLO
}
作用
fn say_hello(to_whom: &str) { //type coercion
println!("Hey {}!", to_whom)
}
fn main(){
let string_slice: &'static str = "you";
let string: String = string_slice.into(); // &str => String
say_hello(string_slice);
say_hello(&string);// &String
}
连接两个字符串
// String is at heap, and can be increase or decrease in its size
// The size of &str is fixed.
fn main(){
let a = "Foo";
let b = "Bar";
let c = a + b; //error
// let c = a.to_string + b;
}
请注意,String和&str是不同的类型,在99%的时间里,您只需要关心&str。
String是动态堆字符串类型,如Vec:当您需要拥有或修改字符串数据时使用它。
str是内存中某个动态长度的UTF-8字节的一个不可更改的1序列。由于大小未知,只能在指针后面处理。这意味着str最常见的形式是&str:对一些UTF-8数据的引用,通常称为“字符串切片”或“切片”。切片只是一些数据的视图,这些数据可以在任何地方,例如。
在静态存储中:字符串“foo”是一个&‘静态字符串。数据被硬编码到可执行文件中,并在程序运行时加载到内存中。在堆分配的String:String中,取消对String数据的&str视图的引用。在堆栈上:例如,下面创建一个堆栈分配的字节数组,然后以&str的形式获取该数据的视图:使用std::str;设x:&[u8]=&[b'a',b'b',b'c'];让stack_str:&str=str::from_utf8(x).unwrap();
总之,如果您需要自己的字符串数据(比如将字符串传递给其他线程,或者在运行时构建它们),请使用String;如果您只需要字符串的视图,请使用&str。
这与向量Vec<T>和切片&[T]之间的关系相同,并且与一般类型的按值T和按引用&T之间的关系相似。
1 A str为固定长度;不能写入超出结尾的字节,或留下尾随无效字节。由于UTF-8是一种可变宽度编码,因此在许多情况下,这有效地迫使所有str都是不可变的。一般来说,突变需要比以前写更多或更少的字节(例如,用ä(2+字节)替换a(1字节)将需要在str中腾出更多空间)。有一些特定的方法可以就地修改&mut str,大多数方法只处理ASCII字符,如make_ASCII_capital。
2自Rust 1.2以来,动态大小的类型允许Rc<str>等引用计数的UTF-8字节序列。Rust 1.21允许轻松创建这些类型。
str类似于String,而不是它的切片。
str是字符串文本,基本上是预先分配的文本:
"Hello World"
该文本必须存储在某个位置,因此它与程序的机器代码一起存储在可执行文件的数据部分中,作为字节序列([u8])。因为文本可以是任意长度,所以它们的大小是动态的:
┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐
│ H │ e │ l │ l │ o │ │ W │ o │ r │ l │ d │
└─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘
┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐
│ 72 │ 101 │ 108 │ 108 │ 111 │ 32 │ 87 │ 111 │ 114 │ 108 │ 100 │
└─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘
我们需要一种方法来访问存储的文本,这就是切片的来源。
切片[T]是内存块的视图。无论是否可变,切片总是借用,这就是为什么它总是在指针&后面。
让我们来解释动态调整大小的含义。一些编程语言,如C,在字符串末尾附加一个零字节(\0),并记录起始地址。要确定字符串的长度,程序必须从起始位置遍历原始字节,直到找到这个零字节。所以,文本的长度可以是任何大小,因此它是动态调整大小的。
然而Rust采用了不同的方法:它使用切片。一个切片存储str开始的地址以及它需要多少字节。它比附加零字节要好,因为在编译期间计算是提前完成的。由于文本可以是任何大小,所以从类型系统的角度来看,它仍然是动态大小的。
因此,“HelloWorld”表达式返回一个胖指针,其中包含实际数据的地址及其长度。这个指针将是我们对实际数据的句柄,它也将存储在我们的程序中。现在数据在指针后面,编译器在编译时知道它的大小。
由于文本存储在源代码中,它将在运行程序的整个生命周期内有效,因此将具有静态生命周期。
所以,“Hello Word”表达式的返回值应该反映这两个特性,它确实:
let s: &'static str = "Hello World";
您可能会问,为什么它的类型写为str而不是[u8],这是因为数据总是保证是有效的UTF-8序列。并非所有UTF-8字符都是单字节,有些字符需要4个字节。所以[u8]是不准确的。
如果您反汇编一个已编译的Rust程序并检查可执行文件,您将看到多个str在数据段中彼此相邻地存储,没有任何指示一个str的开始和另一个strs的结束。
编译器更进一步。如果在程序中的多个位置使用相同的静态文本,Rust编译器将优化程序,并在可执行文件的数据部分中创建一个二进制块,代码中的每个片段都指向该二进制块。
例如,编译器为以下代码创建了一个内容为“Hello World”的连续二进制文件,尽管我们在“Hello世界”中使用了三个不同的文本:
let x: &'static str = "Hello World";
let y: &'static str = "Hello World";
let z: &'static str = "Hello World";
另一方面,字符串是一种特殊类型,将其值存储为u8的向量。以下是在源代码中如何定义字符串类型:
pub struct String {
vec: Vec<u8>,
}
成为向量意味着它是堆分配的,并且可以像任何其他向量值一样调整大小。
专门化意味着它不允许任意访问,并强制执行某些检查,以确保数据始终是有效的UTF-8。除此之外,它只是一个向量。
因此,String是一个可调整大小的缓冲区,用于保存UTF-8文本。此缓冲区在堆上分配,因此可以根据需要或请求进行增长。无论如何,我们都可以填充这个缓冲区。我们可以改变它的内容。
若你们仔细看,vec字段是私有的,以加强有效性。由于它是私有的,我们不能直接创建String实例。之所以将其保持为私有,是因为并非所有字节流都会产生有效的utf-8字符,并且与底层字节的直接交互可能会损坏字符串。我们通过方法创建u8字节,方法运行某些检查。我们可以说,隐私和通过方法控制交互提供了一定的保证。
在String类型上定义了几个方法来创建String实例,new就是其中之一:
pub const fn new() -> String {
String { vec: Vec::new() }
}
我们可以使用它来创建一个有效的字符串。
let s = String::new();
println("{}", s);
不幸的是,它不接受输入参数。所以结果将是有效的,但是一个空字符串,但当容量不足以容纳指定值时,它将像任何其他向量一样增长。但应用程序性能将受到影响,因为增长需要重新分配。
我们可以用不同来源的初始值填充基础向量:
从字符串文本
let a = "Hello World";
let s = String::from(a);
请注意,str仍然被创建,其内容通过String.from复制到堆分配的向量中。如果我们检查可执行二进制文件,我们将在数据部分中看到内容为“Hello World”的原始字节。这是一些人忽略的非常重要的细节。
来自原始零件
let ptr = s.as_mut_ptr();
let len = s.len();
let capacity = s.capacity();
let s = String::from_raw_parts(ptr, len, capacity);
来自角色
let ch = 'c';
let s = ch.to_string();
从字节矢量
let hello_world = vec![72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100];
// We know it is valid sequence, so we can use unwrap
let hello_world = String::from_utf8(hello_world).unwrap();
println!("{}", hello_world); // Hello World
这里我们有另一个重要的细节。向量可能有任何值,但不能保证其内容是有效的UTF-8,因此Rust通过返回Result<String,FromUtf8Error>而不是String来迫使我们考虑这一点。
来自输入缓冲区
use std::io::{self, Read};
fn main() -> io::Result<()> {
let mut buffer = String::new();
let stdin = io::stdin();
let mut handle = stdin.lock();
handle.read_to_string(&mut buffer)?;
Ok(())
}
或者来自实现ToString特性的任何其他类型
由于字符串是引擎盖下的矢量,因此它将显示一些矢量特性:
指针:指针指向存储数据的内部缓冲区。length:长度是当前存储在缓冲区中的字节数。capacity:容量是缓冲区的大小(以字节为单位)。因此,长度总是小于或等于容量。
它将一些财产和方法委托给向量:
pub fn capacity(&self) -> usize {
self.vec.capacity()
}
大多数示例都使用String::from,所以人们会困惑为什么要从另一个字符串创建String。
这是一本很长的书,希望能有所帮助。
对于C#和Java用户:
Rust‘String==StringBuilderRust的&str==(不可变)字符串
我喜欢将&str视为字符串的视图,就像Java/C#中的一个内部字符串,您不能更改它,只能创建一个新字符串。
推荐文章
- 如何在Typescript中解析JSON字符串
- 如何分割逗号分隔的字符串?
- Java字符串—查看字符串是否只包含数字而不包含字母
- 用javascript检查输入字符串中是否包含数字
- Java:检查enum是否包含给定的字符串?
- 用PHP删除字符串的前4个字符
- 如何从字符串的开始或结束删除所有空白?
- 字符串到JS中的对象
- 为什么Python的原始字符串不能以一个反斜杠结尾?
- 我如何读整个文件到性病::字符串在c++ ?
- 从以特定字符开头的字符串中获取子字符串
- 如何用前导零格式化Java字符串?
- Android Split字符串
- 重复字符串到一定长度
- 我如何应用for-each循环到字符串中的每个字符?