rust - 什么时候无法在 Rust 借用检查器中推断生命周期?
问题描述
在大多数情况下,Rust 编译器可以推断生命周期。如果生命周期范围是在运行时确定的,则表示必须明确标记生命周期。
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
这里,
- 生命周期是通用的。
- 这意味着在函数结果返回后,有一个作用域绑定到生命周期 'a。
- 编译器可以知道内存在最短生命周期内有效的信息'a.
我很好奇。而不是使用生命周期语法,编译器不能只采用生命周期 'a 可以绑定的较少范围区域吗?
fn main() { //larger scope
let s1 = String::from("long string is long");
{ //fewer scope
let s2 = String::from("xyz");
let result = longest(s1.as_str(), s2.as_str());
println!("The longest string is {}", result);
}
}
即使调用者端的调用堆栈更复杂,范围区域也是在借用时确定的,所以同样的问题似乎是可能的。
fn func1<'a>(x: &'a str, y: &'a str) {
let c = String::from("hello");
let result = func2 (a,b,c)
...
}
解决方案
我认为您以错误的方式处理问题。在编译诸如main()
您展示的函数时,借用检查器不会检查它调用的各个函数的内容longest()
,例如,它只检查它们的签名。这是一个特性:它允许在不影响其签名提供的保证的情况下更改函数的实现。如果main()
编译成功,您可以确保它会继续编译,但是您修改longest
它,只要您不更改它的声明。
在 的情况下longest()
,未注释的签名是不明确的:
fn longest(x: &str, y: &str) -> &str
// does the above mean:
fn longest<'a, 'b>(x: &'a str, y: &'b str) -> &'a str // returns sub-slice of x
fn longest<'a, 'b>(x: &'a str, y: &'b str) -> &'b str // returns sub-slice of y
fn longest<'c> (x: &'c str, y: &'c str) -> &'c str // returns sub-slice outlived by x and y
fn longest<'a, 'b>(x: &'a str, y: &'b str) -> &'static str // returns static data
如果没有生命周期注释,我们无法仅通过查看声明来判断返回值&str
是来自第一个&str
、第二个&str
、一个共同的生命周期,还是可能是一个静态的&str
。对于非常简单的函数,例如接受并返回单个引用的函数,编译器会执行“生命周期省略”,它会机械地选择未注释签名的“明显”解释,从而允许您编写fn longest(x: &str) -> &str
为fn longest<'a>(x: &'a str) -> &'a str
. 但是当一个函数接受多个引用时,编译器拒绝猜测并让你拼出你想要的东西。
正如答案开头所指出的,这常常使初学者感到困惑,编译器绝对拒绝做的是从函数体推断生命周期签名,因为这会使签名依赖于实现。
推荐阅读
- c - 为什么这个 C 程序(错误地)输出大数的 2 的幂?
- c - Code::Blocks 调试器不会启动(Inferior 1 正常退出)
- java - Bindings.createObjectBinding 和空依赖
- c - c 函数未被调用
- leaflet - React-admin:如何在编辑/创建视图中嵌入传单,用于视觉地址验证
- angular - 在 Angular 组件中访问主题颜色
- ios - 如何打开/关闭 TestFlight 应用程序的定位服务?
- docker - 如何构建私有 docker 映像,然后在一台机器上的 docker-compose 设置中使用它们?
- c# - 如何为在 systemd 上运行的 Ubuntu 正确制作无尽的控制台应用程序 .NET Core 3.1
- apache-spark - 当任务失败的最大尝试次数时会发生什么?