问题
There was some discussion of this in IRC yesterday, which left me feeling vaguely dissatisfied.
The question was:
How do you define a lifetime on a struct to restrict its contents to only things that live as long as 'itself'.
i.e. a 'self sort of thing.
My initial reaction was: you can't.
If you create a struct Foo<'a>, the lifetime associated with it is inferred from the references it contains; unless the struct contains a reference to itself (impossible), you can't have this sort of 'self lifetime.
There was a bunch of chatter about it, and I ended up writing this playpen as a result:
#[deriving(Show)]
struct Bar;
#[deriving(Show)]
struct Foo<'a> {
a:&'a Bar,
b:&'a Bar
}
fn factory<'a>(v1:&'a Bar, v2: &'a Bar) -> Foo<'a> {
return Foo {
a: v1,
b: v2
};
}
fn main() { // <---- Let's call this block lifetime 'one
let a = Bar;
let c = &a; // <-- C has lifetime 'one
{ // <------------ Let's call this block lifetime 'two
let b = Bar;
let mut foo1 = factory(c, c);
foo1.b = &b;
let mut foo2 = factory(&b, &b);
foo2.a = &a;
println!("{}", foo1);
println!("{}", foo2);
}
}
However, I'm now more confused rather than less.
So, in a strict sense in the above:
- c has 'one
- &b has 'two
- 'static > 'one > 'two (that is, 'two is bounded by 'one).
- foo1 has 'one
- foo2 has 'two
Now, my confusion:
Foo<'a> indicates that 'a is the minimum lifetime bound that can be contained by the instance of Foo.
Since 'one > 'two, foo2 should be able contain a &'one a; this works.
Since 'two > 'one, foo1 should not be able to contain &'two b; however' this works.
Why?
It would appear my confusion results from one of two misconceptions; either:
1) The instance of foo1 is in face Foo<'two>, not Foo<'one>.
I don't understand why this would be the case, since it is manufactured in factory<'a> where <'a> is the lifetime of c; which is 'one, not 'two. There's absolutely no way c can be &'two in the example above. The lifetime 'two is not available in the function factory where Foo is created.
2) Struct lifetimes don't work how I understand them to work; ie. a lifetime of 'a on a Foo instance can somehow change between after the instance is created (eg. on a move?)
...but I don't know which one.
回答1:
Lifetime parameters on references are contravariant: they can be substituted with a shorter lifetime when necessary.
Basically, you've got it the wrong way around. When you have a &'one Bar
, you cannot assign a reference to a value with a shorter lifetime (such as 'two
here), otherwise the reference would be dangling when execution leaves the 'two
scope. However, when you have a &'two Bar
, you can assign a reference to a value with a longer lifetime (such as 'one
and 'static
), because the reference will go out of scope before the referent does.
Why does your program compile? The compiler doesn't only use information from the calls to factory
to select the appropriate lifetime; it also uses information from the assignments. &a
has type &'one Bar
and &b
has type &'two Bar
. Because 'two
starts after 'one
and ends before 'one
, the compiler can coerce a &'one Bar
to a &'two Bar
. In object-oriented terms, a &'one Bar
is a &'two Bar
(&'one Bar
is a subtype of &'two Bar
). Just like how in Java, you can pass a String
as an argument to a function that expects an Object
. It's just that the subtyping relation is the other way around for lifetimes.
This means that we've found a common type for &a
and &b
: &'two Bar
. Therefore, the compiler infers 'two
for 'a
in the calls to factory
.
Note that the type of foo2
doesn't change at the assignment; the type of a value is always static.
来源:https://stackoverflow.com/questions/27612687/how-do-lifetime-bounds-on-structs-work-in-rust