rust - 为什么返回字符串切片而不是使用大小会阻止字符串的变异?
问题描述
此代码编译:
fn main() {
let mut s = String::from("some_string");
let n = f1(&s);
s.clear();
println!("n = {}", n);
}
fn f1(s: &String) -> usize {
10
}
fn f2(s: &String) -> &str {
"def"
}
但是,替换对f1()
by的调用f2()
会导致编译失败。在我看来,两者都f1()
做f2()
了一个不可变的借用,并且s.clear()
做了一个可变的借用,所以在这两种情况下我都应该得到编译错误。我错过了什么?
解决方案
这是重现该问题所需的最少代码:
fn f1(s: &String) -> usize { unimplemented!() }
fn f2(s: &String) -> &str { unimplemented!() }
fn main() {
let mut s = String::from("some_string");
let n = f1(&s);
s.clear();
println!("n = {}", n);
}
生命周期分析是基于函数签名执行的。
你会注意到在上面的代码中我使用unimplemented!()
了函数体,问题是完全一样的。这是正常的。
在绝大多数情况下1,函数签名完全指定了函数的接口,无需查看其实现。
作为推论,这也意味着返回类型中的生命周期是否链接到任何参数中的生命周期在签名中完全指定,因此在这种情况下,完整的签名f2
是:
fn f2<'a>(s: &'a String) -> &'a str;
f2
is "def"
(with the 'static
life) 还是&*s
(with the lifetime)的实现'a
无关紧要;只有签名很重要,由于省略规则,签名使用相同的生命周期。
1 我知道的一个例外涉及该-> impl Trait
功能以及生成的对象是否实现Send
或Sync
.
在 的情况下f1
,返回类型未链接到参数,因此参数的借用在对 的调用结束时结束f1
:
fn main() {
let mut s = String::from("some_string");
let n = {
// Immutable borrow of s starts here.
f1(&s)
// Immutable borrow of s ends here.
};
s.clear();
println!("n = {}", n);
}
在 的情况下f2
,返回类型与参数具有相同的生命周期,因此被认为是扩展借用。在 Rust 2015 中,借用将延长,直到返回值超出范围(词法借用);在 Rust 2018 中,借用一直延伸到最后一次使用返回值(非词法借用)。
在您的情况下,两者基本相同:
fn main() {
let mut s = String::from("some_string");
let n = {
// Immutable borrow of s starts here.
f2(&s)
};
s.clear(); // Conflicting attempt to mutably borrow s.
println!("n = {}", n);
// Immutable borrow of s ends here.
}
s.clear()
您可以通过切换和的顺序来观察差异println!
。
推荐阅读
- ios - 如何使用 SwiftUI 将图像拖到图像后面
- sql - 使用聚合和不使用聚合的选择在速度上有什么不同?
- javascript - 单击按钮时执行 2 个功能
- php - WordPress slug 需要页面 URL 中有两个目录
- git - 从开发推送到新的远程分支
- java - Springboot Websocket Client 从具有不同端口的 Websocket 流中读取消息
- angular - 如何组合相似的 rxjs 流?
- python - Python 2D 列表切片,其中一个列表充满了空字符串
- angular - Angular-8 限制导航以防止浏览器挂起
- powershell - powershell - 从字符串中获取一个单词