为什么Rust有String和str?String和之间有什么区别str?什么时候使用String而不是str反之亦然?其中一个被弃用了吗?
huo*_*uon 406
String是动态堆字符串类型,如Vec:需要拥有或修改字符串数据时使用它.
str是一个在内存中某处的动态长度为UTF-8字节的不可变1序列.由于大小未知,因此只能在指针后面处理它.这意味着str最常见的2表现为&str:对某些UTF-8数据的引用,通常称为"字符串片段"或仅称为"片段".切片只是对某些数据的视图,并且该数据可以在任何地方,例如
"foo"是一个&'static str.数据被硬编码到可执行文件中,并在程序运行时加载到内存中.String:String解除对&strString数据视图的引用.在堆栈上:例如,下面创建一个堆栈分配的字节数组,然后获取该数据&str的视图:
use std::str;
let x: &[u8] = &[b'a', b'b', b'c'];
let stack_str: &str = str::from_utf8(x).unwrap();
Run Code Online (Sandbox Code Playgroud)总之,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允许轻松创建这些类型.
Lui*_*uso 78
我有一个C++背景,我发现去想它非常有用String,并&str在C++方面:
String就像一个std::string; 它拥有内存并完成管理内存的肮脏工作.&str就像一个char*(但有点复杂); 它指向一个块的开头,就像你可以得到指向内容的指针一样std::string.他们中的任何一个会消失吗?我不这么认为.它们有两个目的:
String保持缓冲区并且非常实用.&str是轻量级的,应该用于"查看"字符串.您可以搜索,拆分,解析甚至替换块,而无需分配新内存.
&str可以查看一个内部,String因为它可以指向一些字符串文字.以下代码需要将文字字符串复制到String托管内存中:
let a: String = "hello rust".into();
Run Code Online (Sandbox Code Playgroud)
以下代码允许您使用文本本身而不复制(尽管只读)
let a: &str = "hello rust";
Run Code Online (Sandbox Code Playgroud)
Chr*_*gan 37
str,仅用作&str,是一个字符串切片,一个UTF-8字节数组的引用.
String过去是~str一个可增长的,拥有UTF-8字节数组.
Wil*_*een 37
&str和StringString:
String是胖指针(指针+关联的元数据)&str:
'static内存中。&str变量超出范围时,字符串的内存将不会被释放。&str是胖指针(指针+关联的元数据)例子:
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 throughout 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 also change
println!("{}, {}", string2, string2.capacity()); // defghijk, 8
} // memory of string2 on the heap will be freed here because owner goes out of scope
}
Run Code Online (Sandbox Code Playgroud)
00i*_*j00 13
简而言之,String数据类型存储在堆上(就像Vec),并且您可以访问该位置。
&str是切片类型。这意味着它只是对String堆中某个位置已经存在的引用。
&str在运行时不进行任何分配。因此,出于内存原因,您可以使用&strover String。但是,请记住,使用时&str您可能必须处理显式的生命周期。
Zor*_*orf 13
它们实际上完全不同。首先,a str只是类型级别的东西;它只能在类型级别进行推理,因为它是所谓的动态大小类型(DST)。str占用的大小在编译时无法知道,并且取决于运行时信息-无法将其存储在变量中,因为编译器需要在编译时知道每个变量的大小。从str概念上讲,A 只是一行u8字节,并保证它形成有效的UTF-8。行有多大?在运行时之前没人知道,因此无法将其存储在变量中。
有趣的是,在运行时确实存在&str指向strlike的指针或任何其他指针。这就是所谓的“胖指针”;它是带有额外信息的指针(在这种情况下,它是指对象的大小),因此它的大小是其两倍。实际上,a 非常接近a (但不接近)。A 是两个字;一个指针指向a的第一个字节,另一个指针描述a是多少字节。Box<str> &strString&String&strstrstr
与所说的相反,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];
Run Code Online (Sandbox Code Playgroud)
我们有str相同字符串的两个不同子字符串。string是拥有str堆上实际完整缓冲区的缓冲区,&str子字符串只是指向堆上该缓冲区的胖指针。
Ape*_*ion 12
std::String只是 的向量u8。您可以在源代码中找到它的定义。它是堆分配和可增长的。
#[derive(PartialOrd, Eq, Ord)]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct String {
vec: Vec<u8>,
}
Run Code Online (Sandbox Code Playgroud)
str是一种原始类型,也称为字符串 slice。字符串切片具有固定大小。像这样的文字字符串let test = "hello world"具有&'static str类型。test是对这个静态分配的字符串的引用。
&str不能修改,例如
let mut word = "hello world";
word[0] = 's';
word.push('\n');
Run Code Online (Sandbox Code Playgroud)
str确实有可变 slice &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);
Run Code Online (Sandbox Code Playgroud)
但是对 UTF-8 的一个小改动就可以改变它的字节长度,并且一个切片不能重新分配它的所指对象。
snn*_*snn 11
它str类似于String,而不是它的切片,也称为&str。
Anstr是一个字符串文字,基本上是一个预先分配的文本:
"Hello World"
Run Code Online (Sandbox Code Playgroud)
该文本必须存储在某处,因此它与程序的机器代码一起存储在可执行文件的数据部分中,作为字节序列 ([u8])。因为文本可以有任何长度,它们是动态大小的,它们的大小仅在运行时才知道:
+----+-----+-----+-----+-----+----+----+-----+-----+-----+-----+
| H | e | l | l | o | | W | o | r | l | d |
+----+-----+-----+-----+-----+----+----+-----+-----+-----+-----+
+----+-----+-----+-----+-----+----+----+-----+-----+-----+-----+
| 72 | 101 | 108 | 108 | 111 | 32 | 87 | 111 | 114 | 108 | 100 |
+----+-----+-----+-----+-----+----+----+-----+-----+-----+-----+
Run Code Online (Sandbox Code Playgroud)
我们需要访问存储的文本,这就是切片的用武之地。
甲切片,[T]是一个视图到存储器块。无论是否可变,切片总是借用,这就是为什么它总是在指针,后面&。
因此,“Hello World”表达式返回一个胖指针,包含实际数据的地址及其长度。这个指针将是我们实际数据的句柄。现在数据在指针后面,编译器在编译时知道它的大小。
由于文本存储在源代码中,它将在运行程序的整个生命周期内有效,因此将具有static生命周期。
所以,"Hello Word" 表达式的返回值应该反映这两个特征,它所做的:
+----+-----+-----+-----+-----+----+----+-----+-----+-----+-----+
| H | e | l | l | o | | W | o | r | l | d |
+----+-----+-----+-----+-----+----+----+-----+-----+-----+-----+
+----+-----+-----+-----+-----+----+----+-----+-----+-----+-----+
| 72 | 101 | 108 | 108 | 111 | 32 | 87 | 111 | 114 | 108 | 100 |
+----+-----+-----+-----+-----+----+----+-----+-----+-----+-----+
Run Code Online (Sandbox Code Playgroud)
你可能会问为什么它的类型写为 asstr而不是 as [u8],这是因为数据总是保证是有效的 UTF-8 序列。并非所有 UTF-8 字符都是单字节,有些是 4 个字节,并且并非所有字节序列都是有效的 UTF-8 字符。所以 [u8] 是不准确的。
另一方面,String是 u8 字节的专用向量,换句话说,可调整大小的缓冲区保存 UTF-8 文本。我们说专业是因为它不允许任意访问并强制执行某些检查数据始终是有效的 UTF-8。缓冲区在堆上分配,因此它可以根据需要或请求调整其缓冲区的大小。
以下是它在源代码中的定义:
let s: &'static str = "Hello World";
Run Code Online (Sandbox Code Playgroud)
您将能够使用Stringstruct创建字符串,但它vec是私有的以确保有效性和正确检查,因为并非所有字节流都是有效的 utf-8 字符。
但是在 String 类型上定义了几种方法来创建 String 实例,new 是其中之一:
pub struct String {
vec: Vec<u8>,
}
Run Code Online (Sandbox Code Playgroud)
我们可以用它来创建一个有效的字符串。不幸的是它不接受输入参数。所以结果将是有效的,但一个空字符串:
pub const fn new() -> String {
String { vec: Vec::new() }
}
Run Code Online (Sandbox Code Playgroud)
但是我们可以用来自不同来源的初始值填充这个缓冲区:
从字符串文字
let s = String::new();
println("{}", s);
Run Code Online (Sandbox Code Playgroud)
从原材料
let a = "Hello World";
let s = String::from(a);
Run Code Online (Sandbox Code Playgroud)
从一个字符
let ptr = s.as_mut_ptr();
let len = s.len();
let capacity = s.capacity();
let s = String::from_raw_parts(ptr, len, capacity);
Run Code Online (Sandbox Code Playgroud)
从字节向量
let ch = 'c';
let s = ch.to_string();
Run Code Online (Sandbox Code Playgroud)
从输入缓冲区
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
Run Code Online (Sandbox Code Playgroud)
或者来自任何其他实现ToStringtrait 的类型
由于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(())
}
Run Code Online (Sandbox Code Playgroud)
大多数示例都使用String::from,因此人们在思考为什么要从另一个字符串创建 String 时感到困惑。
长文了,希望能帮到你。
Yil*_*maz 10
\n\n\n在这3种不同类型中
\nRun Code Online (Sandbox Code Playgroud)\nlet noodles = "noodles".to_string();\nlet oodles = &noodles[1..];\nlet poodles = "\xe0\xb2\xa0_\xe0\xb2\xa0"; // this is string literal\n\n
\n- \n
字符串有一个可调整大小的缓冲区,用于保存 UTF-8 文本。缓冲区在堆上分配,因此可以根据需要或\n请求调整其缓冲区大小。在示例中,“noodles”是一个拥有近字节缓冲区的字符串,其中七个正在使用中。您可以将字符串视为保证保存格式正确的 UTF-8 的 Vec;事实上,这就是
\nString实现方式。- \n
A
\n&str是对其他人拥有的一系列 UTF-8 文本的引用:它 \xe2\x80\x9cborrows\xe2\x80\x9d 文本。在示例中,oodles 是一个 &str\n,指的是属于“noodles”的文本的最后六个字节,因此\nit 表示文本 \xe2\x80\x9coodles。\xe2\x80\x9d 与其他切片引用一样,&str\ nis afat pointer,包含实际数据的地址和长度。您可以将 a 视为&str只不过是保证保存格式良好的 UTF-8 的 \n&[u8] 。- \n
A
\nstring literal是&str指预分配的文本,通常与程序\xe2\x80\x99s 机器\n代码一起存储在只读存储器中。在前面的示例中,poodles 是一个字符串文字,\n指向程序开始执行时创建的七个字节,并持续到程序退出为止。这就是它们在内存中的存储方式
\n
参考:《Rust 编程》,作者:Jim Blandy、Jason Orendorff、Leonora F。S·廷德尔
\n示例_1.rs
fn main(){
let hello = String::("hello");
let any_char = hello[0];//error
}
Run Code Online (Sandbox Code Playgroud)
示例_2.rs
fn main(){
let hello = String::("hello");
for c in hello.chars() {
println!("{}",c);
}
}
Run Code Online (Sandbox Code Playgroud)
示例_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);
}
Run Code Online (Sandbox Code Playgroud)
Shadowing
fn main() {
let s: &str = "hello"; // &str
let s: String = s.to_uppercase(); // String
println!("{}", s) // HELLO
}
Run Code Online (Sandbox Code Playgroud)
function
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
}
Run Code Online (Sandbox Code Playgroud)
Concat
// 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;
}
Run Code Online (Sandbox Code Playgroud)
请注意,String和&str是不同的类型,并且在 99% 的情况下,您只应该关心&str。
对于 C# 和 Java 人员:
String===StringBuilder &str === (不可变)字符串我喜欢将 a&str视为字符串的视图,就像 Java / C# 中的实习字符串一样,您无法更改它,只能创建一个新字符串。
小智 5
在 Rust 中,str 是一种原始类型,表示 Unicode 标量值序列,也称为字符串切片。这意味着它是字符串的只读视图,并且它不拥有它指向的内存。另一方面,String 是一种可增长、可变、拥有的字符串类型。这意味着当您创建一个 String 时,它将在堆上分配内存来存储字符串的内容,并且当 String 超出范围时它将释放该内存。由于字符串是可增长且可变的,因此您可以在创建字符串后更改其内容。
一般来说,当您想要引用存储在另一个数据结构(例如字符串)中的字符串切片时,请使用 str。当您想要创建并拥有字符串值时,可以使用字符串。
| 归档时间: |
|
| 查看次数: |
40228 次 |
| 最近记录: |