Answers:
String
是动态堆字符串类型,例如Vec
:当您需要拥有或修改字符串数据时使用它。
str
是内存中某处动态长度的UTF-8字节的不变1序列。由于大小未知,因此只能在指针后面处理它。这意味着str
最常见的2表示为&str
:对某些UTF-8数据的引用,通常称为“字符串切片”或仅称为“切片”。切片只是某些数据的视图,该数据可以在任何地方,例如
"foo"
是&'static str
。程序运行时,数据被硬编码到可执行文件中并加载到内存中。String
:String
解除引用到&str
视图中的String
“s的数据。在堆栈上:例如,以下创建堆栈分配的字节数组,然后以形式获取该数据&str
的视图:
use std::str;
let x: &[u8] = &[b'a', b'b', b'c'];
let stack_str: &str = str::from_utf8(x).unwrap();
总之,String
如果需要拥有的字符串数据(例如,将字符串传递到其他线程或在运行时构建它们),请使用;&str
如果仅需要字符串的视图,请使用。
这与向量Vec<T>
和切片之间的关系相同&[T]
,并且与常规类型的按值T
和按引用之间的关系相似&T
。
1 A str
是定长的;您不能将字节写到末尾,也不能保留尾随的无效字节。由于UTF-8是宽度可变的编码,因此str
在许多情况下,这实际上迫使所有s都是不可变的。通常,变异需要写入比以前更多或更少的字节(例如,用(2+字节)替换a
(1字节ä
)将需要在中留出更多空间str
)。有一些可以&str
在适当位置修改的特定方法,大多数是仅处理ASCII字符的方法,例如make_ascii_uppercase
。
2 动态大小类型允许Rc<str>
自Rust 1.2起就进行一系列按引用计数的UTF-8字节的操作。Rust 1.21允许轻松创建这些类型。
[u8; N]
。
Rc<str>
并且Arc<str>
现在可以通过标准库使用。
我有一个C ++背景,我发现去想它非常有用String
,并&str
在C ++方面:
String
像锈std::string
; 它拥有内存并完成管理内存的工作。&str
像char*
(但更复杂);它以指向内容的指针的相同方式将我们指向块的开头std::string
。他们两个都会消失吗?我不这么认为。它们有两个作用:
String
保留缓冲区,非常实用。&str
是轻量级的,应该用于“查看”字符串。您可以搜索,拆分,解析甚至替换块,而无需分配新的内存。
&str
可以查看a的内部,String
因为它可以指向某些字符串文字。以下代码需要将文字字符串复制到String
托管内存中:
let a: String = "hello rust".into();
以下代码可让您直接使用文字本身而不进行复制(尽管只读)
let a: &str = "hello rust";
它们实际上完全不同。首先,a str
只是类型级别的东西;它只能在类型级别进行推理,因为它是所谓的动态大小类型(DST)。str
占用的大小在编译时无法知道,并且取决于运行时信息-无法将其存储在变量中,因为编译器需要在编译时知道每个变量的大小。从str
概念上讲,A 只是一行u8
字节,并保证它形成有效的UTF-8。行有多大?在运行时之前没人知道,因此无法将其存储在变量中。
有趣的是,在运行时确实存在&str
指向str
like的指针或任何其他指针。这就是所谓的“胖指针”;它是带有额外信息的指针(在这种情况下,它是指对象的大小),因此它的大小是其两倍。实际上,a 非常接近a (但不接近)。A 是两个字;一个指针指向a的第一个字节,另一个指针描述a是多少字节。Box<str>
&str
String
&String
&str
str
str
与所说的相反,a str
不必是不变的。如果可以获取&mut str
作为的独占指针,则str
可以对其进行变异,并且对其进行变异的所有安全函数都将确保UTF-8约束得到维护,因为如果违反该约束,那么我们将具有未定义的行为,因为库假定此约束为正确,不检查。
那么什么是String
?这是3个字; 两者与for相同,&str
但是增加了第三个词,即str
堆上缓冲区的容量,始终在堆中(str
必须在堆上)之前管理(在堆上,并且必须重新分配)它管理的缓冲区容量。在String
基本拥有一个str
像他们说的; 它控制它,可以调整它的大小,并在合适时重新分配它。所以String
说a &str
比说a更接近a str
。
另一件事是Box<str>
;它也拥有一个,str
并且其运行时表示形式与a相同,&str
但是它也拥有一个str
与众不同的地方,&str
但是它无法调整大小,因为它不知道其容量,因此基本上a Box<str>
可以看作是String
无法调整大小的固定长度(您可以String
如果您要调整大小,请务必将其转换为)。
除UTF-8约束外[T]
,Vec<T>
其他之间存在非常相似的关系,并且它可以容纳大小不是动态的任何类型。
str
类型类型上的使用主要是使用&str
; 它存在于类型级别,以便能够方便地编写特征。从理论上讲str
,类型不是必须存在的东西,而仅仅&str
是这样,这意味着必须编写许多现在可以通用的额外代码。
&str
超级有用,String
无需复制即可拥有一个的多个不同子字符串;String
如前所述,a 拥有str
它所管理的堆,并且如果您只能String
用新的创建子字符串,String
则必须将其复制,因为Rust中的所有内容只能由一个所有者来处理内存安全性。因此,例如,您可以切片字符串:
let string: String = "a string".to_string();
let substring1: &str = &string[1..3];
let substring2: &str = &string[2..4];
我们有str
相同字符串的两个不同子字符串。string
是拥有str
堆上实际完整缓冲区的缓冲区,&str
子字符串只是指向堆上该缓冲区的胖指针。
std::String
只是的向量u8
。您可以在源代码中找到其定义。它是堆分配的并且可增长。
#[derive(PartialOrd, Eq, Ord)]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct String {
vec: Vec<u8>,
}
str
是基本类型,也称为字符串切片。字符串切片的大小固定。文字字符串如let test = "hello world"
具有&'static str
类型。test
是对此静态分配的字符串的引用。
&str
无法修改,例如,
let mut word = "hello world";
word[0] = 's';
word.push('\n');
str
确实具有可变切片&mut str
,例如:
pub fn split_at_mut(&mut self, mid: usize) -> (&mut str, &mut str)
let mut s = "Per Martin-Löf".to_string();
{
let (first, last) = s.split_at_mut(3);
first.make_ascii_uppercase();
assert_eq!("PER", first);
assert_eq!(" Martin-Löf", last);
}
assert_eq!("PER Martin-Löf", s);
但是对UTF-8的微小更改可以更改其字节长度,并且分片无法重新分配其引用对象。
用简单的话来说,String
数据类型是存储在堆上的(就像Vec
),您可以访问该位置。
&str
是切片类型。这意味着它只是引用String
堆中已经存在的某个地方。
&str
在运行时不进行任何分配。因此,出于内存原因,您可以使用&str
over String
。但是,请记住,使用&str
时可能必须要处理明确的生命周期。
str
是view
的已经存在String
的堆。
对于C#和Java用户:
String
===StringBuilder
&str
===(不可变)字符串我喜欢将a &str
视为对字符串的视图,就像Java / C#中的固定字符串,您无法更改它,只能创建一个新字符串。
这是一个快速简单的解释。
String
-可增长的,可拥有的堆分配数据结构。它可以被强制为&str
。
str
-是(现在,随着Rust的发展)可变的,固定长度的字符串,它驻留在堆或二进制文件中。您只能str
通过字符串切片视图作为借用类型进行交互,例如&str
。
使用注意事项:
身高:String
如果你想拥有或突变的字符串-如字符串传递给另一个线程,等等。
身高:&str
如果你想有一个字符串的只读视图。
&str
是由两个部件组成:一个指向一些字节,和长度。”