23
struct Point {
    x: u32,
    y: u32,
}

struct Line<'a> {
    start: &'a Point,
    end: &'a Point,
}

Here, the only possible option for the start and end fields is to have a lifetime the same or longer than the Line variable that contains them. I can't even imagine how one will go about using a lifetime specifier to say that the fields have a shorter lifespan.

Why do I have to explicitly specify a lifetime here? Is elision not possible in this situation and if so why not?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
RajV
  • 6,860
  • 8
  • 44
  • 62
  • 1
    While I suppose it's possible that Rust could choose to elide the lifetimes in a struct definition, it does not do that now. You will only see the term `lifetime elision` used for functions / methods. – Shepmaster Jan 05 '15 at 18:53

2 Answers2

18

When you define a struct, you aren't making a relation between the lifetime of the struct and the lifetime of the fields. As you point out, the references in the fields have to live longer than the struct.

Instead, what you are doing is providing a "generic lifetime" that will be specialized when you create the struct. This is similar to having a struct with a type parameter:

struct Foo<T>
    foo: T,
}

When you construct the struct, appropriate lifetimes (or types) will be inserted by the compiler, and then it checks that everything still works out.

The other thing is that you can specify the lifetimes with respect to each other:

struct Line<'a, 'b: 'a> {
    start: &'a Point,
    end: &'b Point,
}

This says that start and end can have different lifetimes, so long as the lifetime of end outlives the lifetime of start.

why doesn't the compiler do lifetime elision for structs? It seems in the spirit of Rust to do so

(emphasis mine)

I actually believe that Rust tends towards explicitness, especially when it comes to defining top-level items (like functions, structs).

The rules for lifetime elision for functions have a pretty small scope and were empirically found in RFC 141 to have a high success rate (87%). This was a very good ergonomic return on investment.

Perhaps at some point, similar elision will occur for structs, but it hasn't been a big enough problem yet. If you feel strongly about this, then I'd highly recommend asking for consensus on the user forum, progressing to the developer forum, then ultimately making an RFC.

RFC 2093 adds a small amount of inference. Before it is implemented, you have to express that a generic type as a reference needs to outlive the reference:

struct Foo<'a, T: 'a> {
    start: &'a T,
}

There's no case in which you wouldn't want this bound, so after the RFC is implemented, you can just say:

struct Foo<'a, T> {
    start: &'a T,
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • 4
    It's not clear from this answer why lifetime is *necessary* when declaring a struct. – RajV Jan 05 '15 at 21:03
  • Did you catch the comment I added to your question? If I am understanding you, then the answer is "because the compiler requires it". If you have references in your structure, you have to specify what the lifetime(s) of those references are. There is no lifetime elision for struct declarations, although I suppose that the compiler could automatically put `'a` for every lifetime. To get that, you'd have to make a feature request to Rust itself. – Shepmaster Jan 05 '15 at 21:26
  • 1
    @RajV, because there is no obvious way to go without them. Non-static references inside a struct *necessarily* means that there should be at least one lifetime parameter, but there can be more, and it would mean a different thing. – Vladimir Matveev Jan 05 '15 at 21:27
  • relevant: https://github.com/rust-lang/rfcs/blob/master/text/0141-lifetime-elision.md#lifetime-elision-in-structs – Steve Klabnik Jan 07 '15 at 05:55
  • 2
    @Shepmaster, "There is no lifetime elision for struct declarations, although I suppose that the compiler could automatically put 'a for every lifetime'". The question is precisely that: **why** doesn't the compiler do lifetime elision for structs? It seems in the spirit of Rust to do so, and it seems to me there would be a good reason for this. I don't think your answer addresses that question at all, it only provides examples for explicit lifetime declarations, so I'm very surprised it was accepted. – Garcia Hurtado Feb 12 '15 at 21:38
  • @GarciaHurtado I've added a bit more to my answer, but ultimately the reason is "because it doesn't yet", which I know isn't a satisfying reason. I am unaware of any technical reasons that prevent it though. – Shepmaster Feb 12 '15 at 22:04
  • @Shepmaster, thank you! the additional information you added certainly does help clarify the current state of affairs. – Garcia Hurtado Feb 13 '15 at 02:13
  • 1
    "This says that start and end can have different lifetimes, so long as the lifetime of end outlives the lifetime of start" - I still do not understand why I, as a the author of the struct, should ever impose such a constraint on the consumers of my API. The only constraint I care about, is that the references remain valid for at least as long as the instance of the struct remains valid, which should be implicit and accomplished without explicit lifetime notation. Why would I ever want any other lifetime constraint beyond that? – AxiomaticNexus Apr 22 '19 at 19:46
  • 1
    @AxiomaticNexus you seem to be asking [why have multiple lifetimes in one struct](https://stackoverflow.com/questions/29861388/when-is-it-useful-to-define-multiple-lifetimes-in-a-struct). In other cases, you can just use a generic type which will then have a lifetime within it and you don’t need any explicit lifetimes – Shepmaster Apr 22 '19 at 20:48
  • @Shepmaster That answer you linked makes absolute sense! Finally got this after so long, but now another question arises. When I'm declaring my struct, what should I base my decision on what the lifetime of the references should be? If my struct has implementation methods, then I can base my decision on those methods' requirements; otherwise, should I make each reference have its own lifetime, since that gives the most flexibility to the consumer of he struct? Or should I have all references share the same lifetime? – AxiomaticNexus Apr 23 '19 at 13:15
  • 1
    @AxiomaticNexus now it sounds like you are asking [why ever use one lifetime](https://stackoverflow.com/questions/44709632/)? – Shepmaster Apr 23 '19 at 14:14
  • @Shepmaster So... the answer is: Yes, generally speaking, err for multiple lifetimes unless the implementation block of the struct requires anything stricter. Did I get that right? – AxiomaticNexus Apr 23 '19 at 14:27
  • 2
    @AxiomaticNexus I’d personally stick to one lifetime until something forces me to use multiple. That might change if I was writing a public API that I’d be hesitant to alter after releasing. – Shepmaster Apr 23 '19 at 17:11
3

Suppose we have a constructor for Line:

impl<'a> Line<'a> {
    fn new(start: &'a Point, end: &'a Point) -> Line<'a> { // '
        Line {
            start: start,
            end: end,
        }
    }
}

new returns a Line<'a>. To be able to parameterize a type with a lifetime (as we do here with Line<'a>), this type must define lifetime parameters! Although the compiler could automatically define lifetime parameters when necessary, it's much easier to figure out that a type has lifetime parameters (or not) by just looking at its definition in the source code.

Lifetime parameters on structs and enums play an important role in the borrow checker. They let the compiler know that a struct retains borrows to some values. The compiler can then return errors when you try to mutate a value that has active borrows.

fn main() {
    let mut start = Point { x: 2, y: 4 };
    let end = Point { x: 7, y: 10 };
    let line = Line::new(&start, &end);
    start.x = 3; // error: cannot assign to `start.x` because it is borrowed
}
Francis Gagné
  • 60,274
  • 7
  • 180
  • 155