要了解生命周期,您必须注意它们实际上是类型的一部分,而不是值的一部分。这就是它们被指定为泛型参数的原因。
也就是说,当你写的时候:
fn test(a: &i32) {
let i: i32 = 0;
let b: &i32 = &i;
let c: &'static i32 = &0;
}
那么变量a、b和c实际上是不同的类型:一种是&'__unnamed_1 i32,另一种是&_unnamed_2 i32,另一种是&'static i32。
有趣的是,生命周期创建了一个类型层次结构,因此当一个类型的生命周期比另一种类型的生命周期长,但除了它们是相同的,那么长生命周期的就是短生命周期的子类型.
特别是在极端多重继承的情况下,&'static i32 类型是任何其他 &'_ i32 的子类型。
您可以通过此示例检查 Rust 子类型是否真实:
fn test(mut a: &i32) {
let i: i32 = 0;
let mut b: &i32 = &i;
let mut c: &'static i32 = &0;
//c is a subtype of a and b
//a is a subtype of b
a = c; // ok
c = a; // error
b = a; // ok
a = b; // error
}
值得注意的是,生命周期是一个借用检查器问题。一旦满足并且代码被证明是安全的,生命周期就会被擦除,并且代码生成是盲目的,假设所有对内存值的访问都是有效的。这就是为什么即使生命周期是通用参数,foo<'a>() 也只会实例化一次。
回到你的例子:
fn foo<'a>(x: &'a str, y: &'a str) -> &'a str {
x
}
您可以使用不同生命周期的值调用此函数,因为编译器会将'a 推断为两者中的较短者,因此&'a str 将是另一个的超类型:
let s = String::from("hello");
let r = foo(&s, "world");
这相当于(为生命周期注解发明的语法):
let s: &'s str = String::from("hello");
let r: &'s str = foo::<'s>(&s, "world" as &'s str);
关于具有多个生命周期的结构,这通常无关紧要,而且我通常将所有生命周期声明为相同,特别是如果类型是我的 crate 私有的。
但是对于公共泛型类型,声明多个生命周期可能很有用,特别是因为用户可能希望创建 一些它们'static。
例如:
struct Foo<'a, 'b> {
x: &'a i32,
y: &'b str,
}
struct Bar<'a> {
x: &'a i32,
y: &'a str,
}
Foo的用户可以将其用作:
let x = 42;
let foo = Foo { x: &x, y: "hello" };
但是Bar的用户必须分配一个String,或者做一些堆栈分配的str魔法:
let x = 42;
let y = String::from("hello");
let bar = Bar { x: &x, y: &y };
请注意,Foo 可以像 Bar 一样使用,但不能反过来使用。
此外,impl Foo 可能会提供 impl Bar 无法提供的其他功能:
impl<'a> Foo<'a, 'static> {
fn get_static_str(&self) -> &'static str {
self.y
}
}