我一直在尝试了解 Rust 借用和所有权模型。
假设我们有以下代码:
fn main() {
let a = String::from("short");
{
let b = String::from("a long long long string");
println!("{}", min(&a, &b));
}
}
fn min<'a>(a: &'a str, b: &'a str) -> &'a str {
if a.len() < b.len() {
return a;
} else {
return b;
}
}
min()
只返回对两个引用字符串中较短的字符串的引用。main()
传入两个字符串引用,其引用在不同的作用域中定义。我使用了String::from()
,以便引用没有静态生存期。程序正确打印short
.这是 Rust Playground 中的示例。
如果我们参考 Rustonomicon(我很欣赏这是一个正在进行的文档),我们被告知函数签名的含义如下:
fn as_str<'a>(data: &'a u32) -> &'a str
表示功能:
引用具有一定寿命的
u32
,并承诺它可以产生对可以存活同样长的str
的引用。
现在让我们从我的例子中转向min()
的签名:
fn min<'a>(a: &'a str, b: &'a str) -> &'a str
这更受攻击,因为:
- 我们有两个输入引用。
- 它们的指称在不同的范围内定义,这意味着它们在不同的生命周期内有效(
a
有效期更长)。
使用与上面引用的语句类似的措辞,min()
的功能签名是什么意思?
该函数接受两个引用,并承诺生成对
str
的引用,该引用可以与a
和b
的引用一样长。这感觉不知何故是错误的,就好像我们从min()
返回对b
的引用,那么很明显,该引用在main()
中a
的生命周期内无效。该函数接受两个引用,并承诺生成对
str
的引用,该引用的寿命与a
和b
的两个引用中较短的一样长。这可以工作,因为a
和b
的指称在main()
的内部范围内仍然有效。完全是别的什么?
总而言之,我不明白当调用方的不同范围中定义min()
的两个输入引用的生命周期到同一生命周期意味着什么。
它是 (2):返回的引用寿命与较短的输入生存期一样长。
但是,从函数的角度来看,两个输入生存期实际上是相同的(两者都是'a
)。因此,鉴于来自main()
的变量a
显然比b
寿命更长,这是如何工作的?
诀窍是调用方缩短两个引用之一的生存期以匹配min()
函数签名。如果你有一个引用&'x T
,你可以把它转换成&'y T
iff'x
'y
寿命(也写成:'x: 'y
)。这很直观(我们可以缩短引用的生命周期而不会产生不良后果)。编译器会自动执行此转换。因此,假设编译器将您的main()
转换为:
let a = String::from("short");
{
let b = String::from("a long long long string");
// NOTE: this syntax is not valid Rust!
let a_ref: &'a_in_main str = &a;
let b_ref: &'b_in_main str = &b;
println!("{}", min(&a as &'b_in_main str, &b));
// ^^^^^^^^^^^^^^^^^^
}
这与称为子类型的东西有关,您可以在这个出色的答案中阅读更多相关信息。
总而言之:调用方缩短一个生存期以匹配函数签名,以便函数可以假定两个引用具有相同的生存期。
我要去(3)别的东西!
使用您的函数签名:
fn min<'a>(a: &'a str, b: &'a str) -> &'a str { ...}
// ...
min(&a, &b)
'a
不是借用对象的生存期。 它是编译器为此调用生成的新生存期。a
和b
将在调用所需的时间内借用(或可能重新借用),并通过返回值的范围进行扩展(因为它引用相同的'a
)。
一些例子:
let mut a = String::from("short");
{
let mut b = String::from("a long long long string");
// a and b borrowed for the duration of the println!()
println!("{}", min(&a, &b));
// a and b borrowed for the duration of the expression, but not
// later (since l is not a reference)
let l = min(&a, &b).len();
{
// borrowed for s's scope
let s = min(&a, &b);
// Invalid: b is borrowed until s goes out of scope
// b += "...";
}
b += "..."; // Ok: b is no longer borrowed.
// Borrow a and b again to print:
println!("{}", min(&a, &b));
}
如您所见,任何单个调用的'a
都不同于实际a
的生存期,并且借用了b
,当然两者都必须超过每个调用的生成生存期。
(游乐场)
除了@Lukas在答案中提到的内容外,您还可以将函数的签名读取为 - 返回的引用在两个传递的引用都有效之前有效,即参数生命周期之间的连接(又名 AND)。
它还有更多的东西。下面是两个代码示例:
let a = String::from("short");
{
let c: &str;
let b = String::from("a long long long string");
c = min(&a, &b);
}
和
let a = String::from("short");
{
let b = String::from("a long long long string");
let c: &str;
c = min(&a, &b);
}
第一个不起作用(第二个不起作用)。看起来b
和c
的生存期与它们在同一作用域中的生存期相同,但作用域中的排序也很重要,因为在第一种情况下b
生存期将在c
之前结束。