我正在读“这本书”,想测试一下我对章节切片借阅规则的理解。
我对借用检查器如何将我传入的内容与函数返回的内容关联起来(我的假设)印象深刻。所以我尝试了一些示例来测试我的理解,并且我正在努力弄清楚借用检查器的规则。
- 为什么
test1
编译失败,在哪里test5
可以找到? - 我的理解哪里错了?(见下面的思考过程)
- “这本书”在哪里涵盖了我所不了解的内容?
该代码始终相同:
fn tmp() {
let mut s = String::from("hello world");
let thing = testX(&s);
s.clear(); // error!
let tmp = thing.len();
println!("the first word is: {tmp}");
}
测试 1:
fn test1(s: &String) -> &str {
"Slice"
}
这里我传入了一个可变字符串的不可变引用,但没有在函数中使用它。我返回了一个不同的切片(在我看来,是一个不同的不可变字符串)。我相信一旦test1
返回,它就不再持有对 的借用&s
。我期望这段代码能够编译,但事实并非如此,我仍然以某种方式持有对 的不可变借用s
。
所以我想知道借用检查器是否将返回的类型与传入的类型相关联。
测试2:
fn test2(s: &String) -> Vec<&str> {
let mut vec = Vec::new();
vec.push(s.as_str());
vec
}
我尝试使用更多间接方式来发挥创造力。我不知道as_str()
所有权方面有什么用,但我有兴趣了解。在这里,我传回了传入内容的一部分,因此我并不惊讶它无法编译,但我已经怀疑我对规则的理解是错误的。
测试 3
fn test3(s: &String) -> Vec<&str> {
let mut vec = Vec::new();
vec.push("Slice");
vec
}
我遵循了与 类似的原理test1
,但也无法编译。这告诉我,它与返回传入类型的引用类型无关,除非它将推理扩展到拥有向量的类型。。?
测试 4
fn test4(s: &String) -> Vec<&usize> {
let mut vec = Vec::new();
vec.push(&1);
vec
}
现在我变得狡猾起来,我创建的代码我认为与 相当test3
,但返回的却是不相关的类型。但编译器仍然不允许这样做。所以看起来,由于某种原因,该函数在返回后仍保留着 的借用?!&String
测试 5
fn test5(s: &String) -> Vec<String> {
let mut vec = Vec::new();
vec.push(String::from("something new"));
vec
}
我尝试了这个,完全预料到它也无法编译,但它有效!
test1
我认为它在逻辑上与借用相同s
。在这两种情况下,我都没有对 做任何事情s
,但test1
保留借用,而 则test5
没有。
帮助!
我将首先尝试测试 1 — 希望它也能为您提供有关其他情况的一些见解。
当你这样做时:
... 有一个隐式的生命周期说明符被设置,并且对于两次借用来说都是相同的。就好像你写了:
这意味着,就调用者
test1
所知,生命周期是相同的,并且&str
可以保留传入&String
的某些数据。事实上,这也会编译:从调用者的角度来看,它不知道主体中发生了什么,因此借用检查器必须“假设最坏的情况”,也就是说,主体就像最后一段代码一样。现在您可以看到为什么
s.clear()
会出现错误:您正在清除thing
依赖于的底层内存!你可以通过提供显式的生命周期说明符来解决这个问题。以下两种方法都可以:
您的大多数其他 testX 基本上都是主题的变体:您告诉编译器返回值在某种程度上与输入的生命周期相关。
test5 是个例外:返回值根本不指输入的借用生命周期,而是它自己的、完全拥有的实体。将数据
String::from
复制到新的字符串(而不是引用原始字符串中的数据),并且没有借用这一事实将其形式化为类型系统。这样,类型系统就会说“啊,没有其他人依赖s
,所以我可以自由地修改它。”