为什么Rust有String和str?String和str之间有什么区别?什么时候使用String而不是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只是一个类型级别的东西;它只能在类型级别进行推理,因为它是所谓的动态大小类型(DST)。str所占的大小在编译时无法得知,并且取决于运行时信息-它不能存储在变量中,因为编译器需要在编译时知道每个变量的大小。str在概念上只是一行u8字节,并保证它形成有效的UTF-8。这排有多大?在运行时之前没有人知道,因此它不能存储在变量中。

有趣的是,在运行时确实存在一个&str或任何其他指向str的指针,如Box<str>。这就是所谓的“胖指针”;它是一个带有额外信息的指针(在本例中是指它所指向的对象的大小),因此它的大小是它的两倍。事实上,&str非常接近字符串(但不是&String)。A&str是两个单词;一个指针指向str的第一个字节,另一个数字描述str的长度。

与所说的相反,str不需要是不可变的。如果您可以获取一个&mut str作为str的独占指针,那么您可以对它进行变异,并且所有变异它的安全函数都可以保证支持UTF-8约束,因为如果违反了这一约束,那么我们就有未定义的行为,因为库假定此约束为真,并且不检查它。

那么什么是字符串?这是三个字;两个与&str相同,但它添加了第三个字,即堆上str缓冲区的容量,总是在堆上(str不一定在堆上),它在填充之前管理,并且必须重新分配。String基本上拥有一个str;它控制它,可以调整大小,并在合适时重新分配。因此,正如所说,字符串更接近&str而不是str。

另一件事是Box<str>;它还拥有一个str,其运行时表示形式与&str相同,但它也拥有与&str不同的str,但它无法调整其大小,因为它不知道其容量,所以基本上Box<str>可以被视为一个固定长度的字符串,无法调整大小(如果要调整大小,可以始终将其转换为String)。

[T]和Vec<T>之间存在非常相似的关系,只是没有UTF-8约束,它可以容纳任何大小不是动态的类型。

在类型级别上使用str主要是使用&str;它存在于类型级别,以便能够方便地书写特征。理论上,str作为一种类型的东西不需要存在,只需要&str,但这意味着需要编写很多额外的代码,这些代码现在可以是通用的。

&str非常有用,可以在不复制的情况下拥有一个字符串的多个不同子字符串;正如所说,String拥有它管理的堆上的str,如果您只能用新的String创建String的子字符串,则必须复制它,因为Rust中的所有内容只能有一个所有者来处理内存安全问题。例如,您可以对字符串进行切片:

let string: String   = "a string".to_string();
let substring1: &str = &string[1..3];
let substring2: &str = &string[2..4];

我们有两个相同字符串的不同子字符串str。字符串是拥有堆上实际完整str缓冲区的字符串,&str子字符串只是堆上缓冲区的胖指针。

对于C#和Java用户:

Rust‘String==StringBuilderRust的&str==(不可变)字符串

我喜欢将&str视为字符串的视图,就像Java/C#中的一个内部字符串,您不能更改它,只能创建一个新字符串。

我有C++背景,我发现用C++术语思考String和&str非常有用:

Rust字符串类似于std::String;它拥有内存并执行管理内存的肮脏工作。Rust&str就像char*(但有点复杂);它以同样的方式将我们指向块的开头,您可以获得指向std::string内容的指针。

他们中的任何一个都会消失吗?我不这么认为。它们有两个目的:

字符串保留缓冲区,使用起来非常实用&str是轻量级的,应该用来“查看”字符串。您可以搜索、拆分、解析甚至替换块,而无需分配新的内存。

&str可以查看字符串内部,因为它可以指向某个字符串文本。以下代码需要将文本字符串复制到字符串管理的内存中:

let a: String = "hello rust".into();

以下代码允许您在没有副本的情况下使用文字本身(尽管是只读的):

let a: &str = "hello rust";

锈蚀和字符串(&S)


字符串:

Rust拥有String类型,字符串本身存在于堆中,因此是可变的,可以更改其大小和内容。因为当拥有字符串的变量超出范围时,String是被拥有的,所以堆上的内存将被释放。String类型的变量是胖指针(指针+相关元数据)fat指针长度为3*8字节(字大小),由以下3个元素组成:指向堆上实际数据的指针,它指向第一个字符字符串长度(字符数)堆上字符串的容量

&字符串:

Rust非拥有的String类型,默认情况下是不可变的。字符串本身位于内存中的其他位置,通常位于堆或“静态内存”中。因为当&str变量超出范围时,字符串是非所有的,所以字符串的内存不会被释放。&str类型的变量是胖指针(指针+相关元数据)fat指针长度为2*8字节(字大小),由以下2个元素组成:指向堆上实际数据的指针,它指向第一个字符字符串长度(字符数)

例子:

use std::mem;

fn main() {
    // on 64 bit architecture:
    println!("{}", mem::size_of::<&str>()); // 16
    println!("{}", mem::size_of::<String>()); // 24

    let string1: &'static str = "abc";
    // string will point to `static memory which lives through the whole program

    let ptr = string1.as_ptr();
    let len = string1.len();

    println!("{}, {}", unsafe { *ptr as char }, len); // a, 3
    // len is 3 characters long so 3
    // pointer to the first character points to letter a

    {
        let mut string2: String = "def".to_string();

        let ptr = string2.as_ptr();
        let len = string2.len();
        let capacity = string2.capacity();
        println!("{}, {}, {}", unsafe { *ptr as char }, len, capacity); // d, 3, 3
        // pointer to the first character points to letter d
        // len is 3 characters long so 3
        // string has now 3 bytes of space on the heap

        string2.push_str("ghijk"); // we can mutate String type, capacity and length will aslo change
        println!("{}, {}", string2, string2.capacity()); // defghijk, 8

    } // memory of string2 on the heap will be freed here because owner goes out of scope

}

字符串是一个Object。

&str是对象的一部分的指针。