为什么Rust具有String和str? String和str有什么区别? 什么时候使用String代替str,反之亦然? 其中之一是否已被弃用?
String是动态堆字符串类型,例如Vec:在您需要拥有或修改字符串数据时使用它。
str是动态长度的UTF-8字节的不可变1序列,在内存中的某个位置。由于大小未知,因此只能在指针后面处理它。这意味着str最常见的是2,显示为&str:对某些UTF-8数据的引用,通常称为"字符串切片"或简称为"切片"。切片只是查看某些数据的视图,该数据可以位于任何位置,例如
总之,如果需要拥有的字符串数据(例如,将字符串传递到其他线程或在运行时构建它们),请使用String;如果只需要一个字符串视图,请使用&str。
这与向量Vec< T >和切片&[T]之间的关系相同,并且与一般类型的按值T和按引用&T之间的关系相似。
1 A str是固定长度的;您不能将字节写到末尾,也不能保留尾随的无效字节。由于UTF-8是可变宽度编码,因此在许多情况下,这实际上迫使所有str是不可变的。通常,变异需要比以前写入更多或更少的字节(例如,将a(1个字节)替换为?(2个以上字节)将需要在str中留出更多空间)。有一些可以直接修改&str的特定方法,大多数是仅处理ASCII字符的方法,例如make_ascii_uppercase。
自Rust 1.2起,动态大小类型允许使用Rc之类的东西作为引用计数的UTF-8字节序列。 Rust 1.21允许轻松创建这些类型。
-
" UTF-8字节序列(未知长度)"-这是过时的吗?文档说" &str由两个部分组成:指向某些字节的指针和一个长度。"
-
它不是过时的(表示已经相当稳定),只是有点不精确:它不是静态已知的,与[u8; N]不同。
-
@mrec在编译时是未知的,例如,当创建堆栈框架时,无法假定它的大小。因此,为什么它经常被当作引用,而引用是编译时的已知大小,即指针的大小。
-
更新:Rc和Arc现在可以通过标准库使用。
-
>"在静态存储中:字符串文字" foo"是&static str。在程序运行时,数据被硬编码到可执行文件中并加载到内存中。 ---应该说明它在什么内存中加载。就像是将其存储在堆或堆栈中还是其他地方
-
@cjohansson静态分配的对象通常既不存储在堆中也不存储在堆栈中,而是存储在它们自己的内存区域中。
我有C ++背景,并且发现用C ++术语考虑String和&str非常有用:
-
Rust String就像std::string;它拥有内存并完成管理内存的工作。
-
Rust &str就像char*(但稍微复杂一些);它以类似于获取std::string内容的指针的方式将我们指向了块的开头。
他们两个都会消失吗?我不这么认为。它们有两个作用:
String保留缓冲区,非常实用。 &str是轻量级的,应该用于"查看"字符串。您可以搜索,拆分,解析甚至替换块,而无需分配新的内存。
&str可以指向String的内部,因为它可以指向某个字符串文字。以下代码需要将文字字符串复制到String受管内存中:
1
| let a: String ="hello rust".into(); |
以下代码可让您直接使用文字本身而不进行复制(尽管只读)
1
| let a: &str ="hello rust"; |
-
像string_view?
-
这个类比确实帮助了我,谢谢!
仅用作&str的str是字符串切片,是对UTF-8字节数组的引用。
String是以前的~str,它是一个可增长的拥有的UTF-8字节数组。
-
从技术上讲,以前是~str的现在是Box
-
@ jv110:否,因为~str是可增长的,而Box是不可增长的。 (与其他~对象不同,~str和~[T]可以神奇地增长,这就是为什么引入String和Vec< T >的原因,因此规则都非常简单且一致。)
它们实际上完全不同。首先,str只是类型级别的东西;它只能在类型级别进行推理,因为它是所谓的动态大小类型(DST)。 str占用的大小在编译时无法知道,并且取决于运行时信息-无法将其存储在变量中,因为编译器需要在编译时知道每个变量的大小。从概念上讲,str只是一行u8字节,并保证它形成有效的UTF-8。行有多大?在运行时之前没人知道,因此无法将其存储在变量中。
有趣的是,&str或指向str的任何其他指针(如Box)在运行时确实存在。这就是所谓的"胖指针";它是一个具有额外信息的指针(在这种情况下,它是指向对象的大小),因此它的大小是其两倍。实际上,&str非常接近String(但不接近&String)。 &str是两个词;一个指针指向str的第一个字节,另一个指针描述str的字节数。
与所说的相反,str不需要是不变的。如果您可以获取&mut str作为指向str的排他指针,则可以对其进行变异,并且对其进行变异的所有安全函数都可以保证坚持使用UTF-8约束,因为如果违反该约束,则我们具有未定义的行为,即库假定此约束为true,并且不对其进行检查。
那么什么是String?那是三个字;两者与&str相同,但它添加了第三个字,即堆上str缓冲区的容量,始终在堆上(str不一定在堆上)在堆满之前管理并且必须重新分配。正如他们所说,String基本上拥有一个str;它控制它,可以调整它的大小,并在合适时重新分配它。所以说String比str更接近&str。
另一件事是Box;它也拥有一个str,并且其运行时表示形式与&str相同,但是它也拥有str,与&str不同,但是它无法调整大小,因为它根本不知道其容量,因此基本上是一个Box可以看作是无法调整大小的定长String(如果要调整大小,可以始终将其转换为String)。
[T]和Vec< T >之间存在非常相似的关系,除了没有UTF-8约束并且它可以容纳大小不是动态的任何类型。
在类型级别上使用str的目的主要是使用&str创建通用抽象。它存在于类型级别,以便能够方便地编写特征。从理论上讲,str作为类型的东西不需要存在,而仅需&str即可,但这意味着必须编写许多现在可以通用的额外代码。
&str超级有用,它能够具有String的多个不同子字符串而无需复制;如前所述,String在它管理的堆上拥有str,如果您只能用新的String创建String的子字符串,则必须复制它,因为Rust中的所有内容只能有一个所有者处理内存安全。因此,例如,您可以切片字符串:
1 2 3
| 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的向量。您可以在源代码中找到其定义。它是堆分配的并且可增长。
1 2 3 4 5
| #[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不能被修改,例如,
1 2 3 4
| let mut word ="hello world";
word[0] = 's';
word.push('
'); |
str确实具有可变切片&mut str,例如:
pub fn split_at_mut(&mut self, mid: usize) -> (&mut str, &mut str)
1 2 3 4 5 6 7 8
| 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在运行时不进行任何分配。因此,出于内存原因,可以在String上使用&str。但是,请记住,使用&str时,您可能必须处理显式的生存期。
-
堆中的某处-并不完全准确。
-
我的意思是str是堆中已经存在的String的view。
-
我明白那是您的意思,而林说那不是完全准确。"堆"不是语句的必需部分。
对于C#和Java用户:
-
锈'String === StringBuilder
-
Rust的&str ===(不可变)字符串
我喜欢将&str看作是字符串的视图,就像Java / C#中的固定字符串,您不能更改它,只能创建一个新字符串。
-
Java / C#字符串与Rust字符串之间的最大区别在于,Rust确保该字符串是正确的unicode,因此,要使字符串中的第三个字符需要更多的思考,而不仅仅是" abc" [2]。 (鉴于我们生活在一个多语言的世界中,这是一件好事。)
-
这是不正确的。可变性主题已在投票最多的答案中得到了解决;请阅读以了解更多信息。
这是一个快速简单的解释。
String-可增长的,可拥有的堆分配数据结构。 可以将其强制为&str。
str-是(现在,随着Rust的发展)可变的,固定长度的字符串,它驻留在堆或二进制文件中。 您只能通过字符串切片视图(例如&str)作为借用类型与str进行交互。
使用注意事项:
如果您想拥有或更改一个字符串(例如,将该字符串传递到另一个线程等),请首选String。
如果要具有字符串的只读视图,请首选&str。
-
这是不正确的。可变性主题已在投票最多的答案中得到了解决;请阅读以了解更多信息。