刚开始生 rust 的人。在我阅读第4.3章后,我对第4.3章的内容感到困惑,因为该章与该原则有交叉引用
在任何给定的时间,您可以有一个可变引用或任意数量的不可变引用。
简单的例子是
fn main() {
let mut str: String = String::from("hello");
let slice: &str = &str[0..2]; // #1
str.clear(); // #2
println!("{}", slice);
}
此示例在编译时会导致错误。
error[E0502]: cannot borrow `str` as mutable because it is also borrowed as immutable
--> src/main.rs:4:5
|
3 | let slice: &str = &str[0..2]; // #1
| --- immutable borrow occurs here
4 | str.clear(); // #2
| ^^^^^^^^^^^ mutable borrow occurs here
5 | println!("{}", slice);
| ----- immutable borrow later used here
教程注解说原因是它违反了上面的原则。但是,我不能理解。在我看来,#1
创建了一个类型为**&str的不可变引用,相反,#2
创建了一个类型为&String**的可变引用,根据类型,它们似乎引用的东西不一样,因为它们有不同的引用类型。为什么它违反了上面的原则,似乎只适用于相同类型的引用?有没有什么原则可以澄清这个问题?
2条答案
按热度按时间u4vypkhs1#
我想你误会了。
String
**不是str
的可变版本。这是它自己的类型。let mut x: String
是let x: String
的可变版本。String
是 owned,可以修改。str
是一个“slice”类型,指的是一个字符串的内容,或者在String
内部,或者在全局内存中作为&'static str
。没有
mut str
,因为str
根据定义是对字符串中不可变部分的引用。让我们看看你的代码。(将
str
重命名为s
,因为这太混乱了)这里没有
&String
。通过&s[0..2]
获取String
的切片会自动创建&str
,因为String
的规范是这样说的:fn index(&self,index:Range)-> &str
为什么它违反了上面的原则,似乎只适用于相同类型的引用?
这是不正确的。它们不一定是同一类型。如果你持有一个
&str
,它引用了一个String
的内容,那么当&str
引用存在时,String
对象也会被阻止变异。你甚至可以在其他对象中存储引用,然后这些对象的存在仍然会阻止原始的String
。它们绝对是不同的物体
但这并不意味着他们不能联系在一起。
要演示两个不同类型的对象可以具有连接的生存期,请查看以下代码:
错误信息非常清楚:
请注意,
B
类型附加了生存期'a
。这个生存期将在示例化时由编译器自动导出,并用于防止只要B
存在,引用的A
对象的可变使用。&str
还附加了一个生存期,用于防止对引用的String
对象的可变访问。kiz8lqtg2#
你可以把Rust中的
String
看作是包含三个数据块--一个指针(指向[堆上]的一个分配的内存块,它包含一个连续的字节序列--本质上是一个堆分配的u8
数组),一个整数,它存储上述内存块的容量(也就是内存块的大小)。缓冲区的大小),以及存储字符串大小的整数(即,实际上使用了多少缓冲器)。当您从
String
创建切片(&str
对象)时,切片仍将指向String
对象保存的数据。对于所有意图和目的,组成切片的数据是一个const
(使用C语言的说法)指针和一个指示切片立即大小的整数(它没有提供有关底层缓冲区大小的信息)。在最初的文章中,...slice
变量引用str
保存的数据(作为不可变的借用)。如果你再看看
String
对象的clear
方法的签名行.您可以看到,对
clear
的方法调用涉及到调用对象的不可变引用。因此,一旦调用了clear
方法,通过slice
对数据的任何访问都将消失。方法调用中的可变引用导致了一个可变借用。slice
变量不再借用数据。这就是为什么Rust编译器会抛出错误的原因,这是println!
调用的结果。即使在C或C++中,你的程序也是一个糟糕的举动,因为你试图访问你刚刚清除的数据。这可能类似于使用悬空指针访问释放的内存。这些都是Rust的数据所有权/数据借用模型试图防止的许多类型的内存错误。
上面的代码编译和运行没有错误。但是,重要的是要认识到,
slice
只通过对println!
的第一次调用有效地借用数据。在此之后,由于调用clear
,会有一个临时的可变借用,然后所有权返回给str。重要的是要记住,你可以有尽可能多的不可变引用到一个对象,只要你喜欢。然而,一旦您有了一个可变借用(一个可变引用),那么您所有的不可变借用都将被没收(您不能再次使用它们)。
当然,没有什么能阻止你创造新的借款!
因此,正如您所看到的,这激发了关于引用的生命周期(借用的生命周期)的整个讨论。因为,正如你所观察到的,它们并不是无限期地活着。
为了解决这个问题,为什么
&str
和&String
被视为对相同值的引用-答案是它们不是。它们都可以保存指向同一个数据数组的指针(即,它们可以具有共同的数据成员,即指针)。但是,原则上,其其余数据是独立的。此外,您可以在本地定义
&str
变量,这些变量被赋值为原始字符串文字。这些变量将完全存在于堆栈中。它们提供了一种使用方便的不可变数据完成许多常见字符串任务的方法-而不必使用任何String
对象机制。但是,每当您希望数据在堆栈之外持久化,或者希望能够改变数据时,您就进入了String
对象特别有用的领域。总之,
&str
对象作为不可变的轻量级对象非常有用。此外,由于它们是轻量级和灵活的,它们也是处理对String
对象的不可变引用的好方法。