当输入非常清楚时,为什么借用检查器需要输出生命周期标签?
Why does borrow checker need life time tags for output when the inputs are very clear?
为什么借用检查器会对以下代码中的生命周期感到困惑
fn main() {
let ss = "abc"; // lets say 'a scope
let tt = "def"; // lets say 'b scope
let result = func(ss, tt);
}
fn func(s: &str, t: &str) -> &str {
t
}
| fn func(s: &str, t: &str) -> &str {
| ^ expected lifetime parameter
|
= help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `s` or `t`
为什么这段代码中的内容如此重要?我是否遗漏了一些非常重要的边缘案例?
但是当我用生命周期标签注释它们时它起作用了。
fn func<'a>(s: &'a str, t: &'a str) -> &'a str {
t
}
我读到每个变量绑定 (let) 都会创建一个隐式作用域,那么为什么 2 个输入变量具有相同的作用域。纠正我,如果我 worng。在函数调用'func'栈中,"s"会先被压入,然后"t",所以"s"和"t"的生命周期不同。首先删除 "t",然后删除 "s"。
你没有告诉编译器 return 值是否可以从 s
、t
借用,或者两者都借用:
fn from_s<'a, 'b>(s: &'a str, t: &'b str) -> &'a str {
// can be abbreviated: fn from_s<'a>(s: &'a str, t: &str) -> &'a str
s
}
fn from_t<'a, 'b>(s: &'a str, t: &'b str) -> &'b str {
// can be abbreviated: fn from_t<'a>(s: &str, t: &'a str) -> &'a str
t
}
fn from_both<'a>(s: &'a str, t: &'a str) -> &'a str {
if s < t {
s
} else {
t
}
}
fn from_neither<'a, 'b>(s: &'a str, t: &'b str) -> &'static str {
// can be abbreviated: fn func(s: &str, t: &str) -> &'static str
"foo"
}
如果你没有写 'static
,编译器会假设最后一个不是你想要的。但是你仍然需要在前三个之间消除歧义。
要了解差异为何重要,请考虑像
这样的来电者
fn main() {
let s = String::from("s");
let r;
{
let t = String::from("t");
r = from_s(&s, &t);
// t goes out of scope
}
println!("{}", r);
}
如果编译器允许您调用 from_t
而不是 from_s
,您将打印一个已经被释放的字符串。
如果我理解正确,问题是 "why both arguments may have the same lifetime?" 简短的回答是生命周期注释不是 具体 值,而是 范围 - 它表示 "this value must live no more/no less then this lifetime".
当您像问题中那样编写代码时:fn func<'a>(s: &'a str, t: &'a str) -> &'a str
,您实际上是在说以下内容:
- 有一些生命周期 - 让我们将其命名为
'a
,每个调用站点上的生命周期可能不同。
- 参数
s
和 t
必须同时存在 不少于 然后 'a
(对于字符串文字,情况总是如此,因为它们是 'static
,但这可能不适用于 &String
强制转换为 &str
)——也就是说,函数类型是 contra 参数类型的变体(生命周期是类型的一部分)。
- return 值必须 不再存在 然后
'a
- 函数类型是 co 变体 return类型。
(有关方差的更多信息,请参阅 Rustonomicon)
简化,这意味着两个参数都必须比 return 值长。这并不总是你想要的 - 考虑以下情况(请注意,我现在 returning s
,因此初始化顺序不会改变):
fn main() {
let ss = "abc";
let mut result = "";
{
let tt = "def".to_string();
result = func(ss, &tt);
}
println!("{}", result);
}
fn func<'a>(s: &'a str, t: &'a str) -> &'a str {
s
}
此代码无法编译,尽管它在逻辑上是正确的,因为生命周期注释不符合逻辑:第二个参数 t
与 return 没有任何关系值,但根据函数注释,它限制了它的生命周期。但是当我们将函数更改为以下内容时:
fn func<'a, 'b>(s: &'a str, t: &'b str) -> &'a str {
s
}
...它编译并 return 期望的结果(尽管有一些警告),因为现在生命周期 'b
与 'a
无关,事实上,完全可以删除 - 生命周期省略会很好地发挥作用。
为什么借用检查器会对以下代码中的生命周期感到困惑
fn main() {
let ss = "abc"; // lets say 'a scope
let tt = "def"; // lets say 'b scope
let result = func(ss, tt);
}
fn func(s: &str, t: &str) -> &str {
t
}
| fn func(s: &str, t: &str) -> &str {
| ^ expected lifetime parameter
|
= help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `s` or `t`
为什么这段代码中的内容如此重要?我是否遗漏了一些非常重要的边缘案例?
但是当我用生命周期标签注释它们时它起作用了。
fn func<'a>(s: &'a str, t: &'a str) -> &'a str {
t
}
我读到每个变量绑定 (let) 都会创建一个隐式作用域,那么为什么 2 个输入变量具有相同的作用域。纠正我,如果我 worng。在函数调用'func'栈中,"s"会先被压入,然后"t",所以"s"和"t"的生命周期不同。首先删除 "t",然后删除 "s"。
你没有告诉编译器 return 值是否可以从 s
、t
借用,或者两者都借用:
fn from_s<'a, 'b>(s: &'a str, t: &'b str) -> &'a str {
// can be abbreviated: fn from_s<'a>(s: &'a str, t: &str) -> &'a str
s
}
fn from_t<'a, 'b>(s: &'a str, t: &'b str) -> &'b str {
// can be abbreviated: fn from_t<'a>(s: &str, t: &'a str) -> &'a str
t
}
fn from_both<'a>(s: &'a str, t: &'a str) -> &'a str {
if s < t {
s
} else {
t
}
}
fn from_neither<'a, 'b>(s: &'a str, t: &'b str) -> &'static str {
// can be abbreviated: fn func(s: &str, t: &str) -> &'static str
"foo"
}
如果你没有写 'static
,编译器会假设最后一个不是你想要的。但是你仍然需要在前三个之间消除歧义。
要了解差异为何重要,请考虑像
这样的来电者fn main() {
let s = String::from("s");
let r;
{
let t = String::from("t");
r = from_s(&s, &t);
// t goes out of scope
}
println!("{}", r);
}
如果编译器允许您调用 from_t
而不是 from_s
,您将打印一个已经被释放的字符串。
如果我理解正确,问题是 "why both arguments may have the same lifetime?" 简短的回答是生命周期注释不是 具体 值,而是 范围 - 它表示 "this value must live no more/no less then this lifetime".
当您像问题中那样编写代码时:fn func<'a>(s: &'a str, t: &'a str) -> &'a str
,您实际上是在说以下内容:
- 有一些生命周期 - 让我们将其命名为
'a
,每个调用站点上的生命周期可能不同。 - 参数
s
和t
必须同时存在 不少于 然后'a
(对于字符串文字,情况总是如此,因为它们是'static
,但这可能不适用于&String
强制转换为&str
)——也就是说,函数类型是 contra 参数类型的变体(生命周期是类型的一部分)。 - return 值必须 不再存在 然后
'a
- 函数类型是 co 变体 return类型。
(有关方差的更多信息,请参阅 Rustonomicon)
简化,这意味着两个参数都必须比 return 值长。这并不总是你想要的 - 考虑以下情况(请注意,我现在 returning s
,因此初始化顺序不会改变):
fn main() {
let ss = "abc";
let mut result = "";
{
let tt = "def".to_string();
result = func(ss, &tt);
}
println!("{}", result);
}
fn func<'a>(s: &'a str, t: &'a str) -> &'a str {
s
}
此代码无法编译,尽管它在逻辑上是正确的,因为生命周期注释不符合逻辑:第二个参数 t
与 return 没有任何关系值,但根据函数注释,它限制了它的生命周期。但是当我们将函数更改为以下内容时:
fn func<'a, 'b>(s: &'a str, t: &'b str) -> &'a str {
s
}
...它编译并 return 期望的结果(尽管有一些警告),因为现在生命周期 'b
与 'a
无关,事实上,完全可以删除 - 生命周期省略会很好地发挥作用。