# Can the lifetimes be simpler?

**URL:** <https://internals.rust-lang.org/t/can-the-lifetimes-be-simpler/22493>\
**Category:** language design\
**Created:** [March 3, 2025, 6:36am UTC](https://internals.rust-lang.org/t/can-the-lifetimes-be-simpler/22493 "2025-03-03T06:36:23Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![SparrowLii](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/sparrowlii/32/7402_2.png) [@SparrowLii](https://internals.rust-lang.org/u/SparrowLii)\
**Post date:** [March 3, 2025, 6:36am UTC](https://internals.rust-lang.org/t/can-the-lifetimes-be-simpler/22493/1 "2025-03-03T06:36:23Z")

</div>

In current Rust, lifetimes are sometimes redundant. For example, in the following code, the compiler should be able to automatically infer that `s1`, `s2` and the return value in `longest` have the same lifetime.

```rust
fn longest(s1: &str, s2: &str) -> &str {
    if s1.len() > s2.len() {
        s1
    } else {
        s2
    }
}

fn main() {
    let string1 = String::from("Rust");
    let string2 = String::from("Programming");

    let result = longest(&string1, &string2);
    println!("The longest string is: {}", result);
}

```

However currently it has to be specified manually:

```rust
fn longest<'a>(s1: &'a str, s2: &'a str) -> &'a str {
    if s1.len() > s2.len() {
        s1
    } else {
        s2
    }
}

```

---

<div class="post-metadata">

**Author:** ![SparrowLii](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/sparrowlii/32/7402_2.png) [@SparrowLii](https://internals.rust-lang.org/u/SparrowLii)\
**Post date:** [March 3, 2025, 6:50am UTC](https://internals.rust-lang.org/t/can-the-lifetimes-be-simpler/22493/2 "2025-03-03T06:50:30Z")

</div>

And in this case, the compiler should automatically infer the lifetime of the return value as the same as `s1`

```rust
fn foo(s1: &str, s2: &str) -> &str {
    println!("{}", s2);
    s1
}

```

Instead of having to manually infer like this:

```rust
fn foo<'a>(s1: &'a str, s2: &str) -> &'a str {
    println!("{}", s2);
    s1
}

```

---

<div class="post-metadata">

**Author:** ![FZs](https://avatars.discourse-cdn.com/v4/letter/f/85f322/32.png) [@FZs](https://internals.rust-lang.org/u/FZs)\
**Post date:** [March 3, 2025, 7:13am UTC](https://internals.rust-lang.org/t/can-the-lifetimes-be-simpler/22493/3 "2025-03-03T07:13:19Z")

</div>

Lifetimes are determined based only on the _signature_ of the function, not the body\[1\].

This allows Rust to borrow-check each function in isolation.

Besides that, it also eliminates a semver hazard (changing the body of a function such that it silently changes the lifetimes in the signature).

* * *

1. even elided lifetimes can be determined purely syntactically from the signature

---

<div class="post-metadata">

**Author:** ![SparrowLii](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/sparrowlii/32/7402_2.png) [@SparrowLii](https://internals.rust-lang.org/u/SparrowLii)\
**Post date:** [March 3, 2025, 7:48am UTC](https://internals.rust-lang.org/t/can-the-lifetimes-be-simpler/22493/4 "2025-03-03T07:48:26Z")

</div>

Yea, the current lifetime reduces the design burden on the compiler. But on the other hand, based on the experience from Rust developers around, lifetimes bring a significant burden on users.

This is not very friendly to expanding the use of this language, especially for who want to write large projects with Rust. The `tcx` that is everywhere in the compiler's code is an example.

So I wonder if we can shift part of the user's burden to the compiler's automatic inference.

---

<div class="post-metadata">

**Author:** ![quinedot](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/quinedot/32/7294_2.png) [@quinedot](https://internals.rust-lang.org/u/quinedot)\
**Post date:** [March 3, 2025, 9:08am UTC](https://internals.rust-lang.org/t/can-the-lifetimes-be-simpler/22493/5 "2025-03-03T09:08:05Z")

</div>

Not being able to tell what the lifetime relationships are based on the function signature is a _deteriment_ to any Rust developer other than the developer actively writing the function --- including the same developer one month later. Having to read the function body in able to ~~know~~ try to infer the lifetime relationships is a burden on the developer.

An IDE being able to infer the relationships and supply the function signature for the actively writing developer -- that's a different story. _That_ is where the desired functionality belongs.

---

<div class="post-metadata">

**Author:** ![Vorpal](https://avatars.discourse-cdn.com/v4/letter/v/aca169/32.png) [@Vorpal](https://internals.rust-lang.org/u/Vorpal)\
**Post date:** [March 3, 2025, 9:11am UTC](https://internals.rust-lang.org/t/can-the-lifetimes-be-simpler/22493/6 "2025-03-03T09:11:18Z")

</div>

> [@SparrowLii](#):
>
> But on the other hand, based on the experience from Rust developers around, lifetimes bring a significant burden on users.

Do you have a source for that statement? Some survey perhaps?

---

<div class="post-metadata">

**Author:** ![SparrowLii](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/sparrowlii/32/7402_2.png) [@SparrowLii](https://internals.rust-lang.org/u/SparrowLii)\
**Post date:** [March 3, 2025, 10:01am UTC](https://internals.rust-lang.org/t/can-the-lifetimes-be-simpler/22493/7 "2025-03-03T10:01:44Z")

</div>

Not at current, but this sounds a good idea, probably I can do a survey in the next few days

---

<div class="post-metadata">

**Author:** ![SparrowLii](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/sparrowlii/32/7402_2.png) [@SparrowLii](https://internals.rust-lang.org/u/SparrowLii)\
**Post date:** [March 3, 2025, 10:08am UTC](https://internals.rust-lang.org/t/can-the-lifetimes-be-simpler/22493/8 "2025-03-03T10:08:15Z")

</div>

According to this logic, the lifetimes should be like comments. We encourage everyone to write it, but we can relax the restrictions so that learners can learn, try and write the code faster.

---

<div class="post-metadata">

**Author:** ![FZs](https://avatars.discourse-cdn.com/v4/letter/f/85f322/32.png) [@FZs](https://internals.rust-lang.org/u/FZs)\
**Post date:** [March 3, 2025, 10:32am UTC](https://internals.rust-lang.org/t/can-the-lifetimes-be-simpler/22493/9 "2025-03-03T10:32:11Z")

</div>

> [@SparrowLii](#):
>
> According to this logic, the lifetimes should be like comments. We encourage everyone to write it, but we can relax the restrictions so that learners can learn, try and write the code faster.

While allowing inferred lifetimes may help learners in the simplest cases, imo it would just move the difficulty further down the line. In more complicated cases where the compiler can't infer the correct lifetimes (I assume there would be such cases), having not learned how lifetimes work in simpler cases, beginners might have an even harder time fixing the problem.

Having an IDE code action insert the lifetime annotations (as @quinedot suggested) would be a better solution for learners of the language too, because they could see what they should've written to make the code work, which makes them more likely to learn how lifetime annotations work\[1\] before hitting the harder cases.

* * *

1. than not having to write lifetime annotations at all

---

<div class="post-metadata">

**Author:** ![SparrowLii](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/sparrowlii/32/7402_2.png) [@SparrowLii](https://internals.rust-lang.org/u/SparrowLii)\
**Post date:** [March 3, 2025, 12:21pm UTC](https://internals.rust-lang.org/t/can-the-lifetimes-be-simpler/22493/10 "2025-03-03T12:21:56Z")

</div>

In fact, my ultimate goal is to have only a few special cases that require users to annotate lifetimes, and let the compiler infer most other cases. Like you said, if only a small number of cases can be simplified, then there is indeed little value.

However, I still feel that the original purpose of lifetimes is to reduce the burden on the compiler, rather than asking users to understand the lifetimes of variables via manual declarations. It has been more than ten years since Rust 1.0 was released, and I think the designers of the language and compiler should really re-cosider this feature.

---

<div class="post-metadata">

**Author:** ![bjorn3](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/bjorn3/32/2736_2.png) [@bjorn3](https://internals.rust-lang.org/u/bjorn3)\
**Post date:** [March 3, 2025, 12:30pm UTC](https://internals.rust-lang.org/t/can-the-lifetimes-be-simpler/22493/11 "2025-03-03T12:30:28Z")

</div>

Rustc already infers lifetimes based on the function signature according to a couple simple rules. ([Lifetime elision - The Rust Reference](https://doc.rust-lang.org/reference/lifetime-elision.html)) Inferring them based on the body is going to be a semver hazard as any change to a function body can accidentally cause the public api to change.

---

<div class="post-metadata">

**Author:** ![SparrowLii](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/sparrowlii/32/7402_2.png) [@SparrowLii](https://internals.rust-lang.org/u/SparrowLii)\
**Post date:** [March 3, 2025, 12:43pm UTC](https://internals.rust-lang.org/t/can-the-lifetimes-be-simpler/22493/12 "2025-03-03T12:43:24Z")

</div>

Yea, semver hazard is an important consideration. But letting the compiler infer (assuming it can) is no more restrictive than manual annotations, so when users need to change their code, they won't make the project more complicated than if they had manually annotated it.

---

<div class="post-metadata">

**Author:** ![FZs](https://avatars.discourse-cdn.com/v4/letter/f/85f322/32.png) [@FZs](https://internals.rust-lang.org/u/FZs)\
**Post date:** [March 3, 2025, 1:31pm UTC](https://internals.rust-lang.org/t/can-the-lifetimes-be-simpler/22493/13 "2025-03-03T13:31:18Z")

</div>

> [@SparrowLii](#):
>
> But letting the compiler infer (assuming it can) is no more restrictive than manual annotations, so when users need to change their code, they won't make the project more complicated than if they had manually annotated it.

I think you're missing the point here. The function signature forms a contract between the caller and the callee (function body). If you make the lifetimes less restrictive to the callee, that means more restrictive to the caller and vice versa.

If I have a function like this:

```rust
fn foo(s1: &str, s2: &str) -> &str {
    s1
}

```

and someone writes a function like this:

```rust
fn main() {
    let s1 = "s1".to_string();
    let s2 = "s2".to_string();

    let r = foo(&s1, &s2);

    drop(s2);

    println!("{r}");
}

```

having inference would mean that I can change the body of `foo`\[1\] to be:

```rust
fn foo(s1: &str, s2: &str) -> &str {
    if s1.len() > s2.len() {
        s1
    } else {
        s2
    }
}

```

...which would break `main` downstream.

Without inference, such a breaking change is not possible.\[2\]

* * *

1. without touching its signature 

2. except for RPIT leaking auto-traits, which is already unfortunate enough

---

<div class="post-metadata">

**Author:** ![robinm](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/robinm/32/6255_2.png) [@robinm](https://internals.rust-lang.org/u/robinm)\
**Post date:** [March 3, 2025, 4:17pm UTC](https://internals.rust-lang.org/t/can-the-lifetimes-be-simpler/22493/14 "2025-03-03T16:17:13Z")

</div>

I see a couple of options:

1. Do nothing (that’s probably the best thing to do).
2. Assume that if there are multiple arguments with references, then references in the output must outlive all references passed as arguments (ie. add a shared `'anonymous` to all references), possibly behind a `#![output_must_outlive_all_arguments]` switch used during development, just like `#![unused_code]` and the like.
3. Ensure that both rust-analyzer and rustc have a "fix-me" button that uses inference and modify the source code to add lifetime annotation accordingly.

I assume that a combination of 2 and 3 could slightly improve the developer experience, but I’m not even sure 2 would really be that useful.

---

<div class="post-metadata">

**Author:** ![dlight](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/dlight/32/8462_2.png) [@dlight](https://internals.rust-lang.org/u/dlight)\
**Post date:** [March 3, 2025, 5:49pm UTC](https://internals.rust-lang.org/t/can-the-lifetimes-be-simpler/22493/15 "2025-03-03T17:49:46Z")

</div>

Per step 2 of Niko's awesome [The borrow checker within](https://smallcultfollowing.com/babysteps/blog/2024/06/02/the-borrow-checker-within/#step-2-a-syntax-for-lifetimes-based-on-places), maybe some day you will be able to write this as

```plaintext
fn longest(s1: &str, s2: &str) -> &'(s1, s2) str {
    if s1.len() > s2.len() {
        s1
    } else {
        s2
    }
}

```

(disabling syntax highlighting because the parser botched it)

I think this is a big improvement because the `'a` doesn't really mean anything, it's just a placeholder for a generic parameter - but this function ought to not be generic! We just lack the syntax to specify that "the output will borrow either from `s1` or from `s2`", which is what `&'(s1, s2) str` means.

And IMO this is probably more intuitive and teachable than describing the same thing using generics: when you write `fn longest<'a>(s1: &'a str, s2: &'a str) -> &'a str` you crucially are depending on the compiler to _unify_ the lifetimes of `s1` and `s2`, which means that `'a` is the _smaller_ (or intersection) of the corresponding unconstrained lifetimes, which is IMO very subtle until you have internalized this concept - and then suddenly it becomes second nature and you are puzzled why people are confused about it.

Note that while simpler and easier to understand (IMO), this syntax is not the same as inferring the lifetimes: you still need to explicitly state which parameters exactly the output can borrow.

But... perhaps we could infer lifetimes of private functions, under the rationale that if we change the body of the function it won't break any crate that depend on it. But even then, I think that there should be explicit syntax to infer it, like this:

```rust
fn longest(s1: &str, s2: &str) -> &'_ str {
    if s1.len() > s2.len() {
        s1
    } else {
        s2
    }
}

```

And if you add a `pub` to this function the compiler would of course refuse to compile, but also _print the lifetime it inferred_ so you can paste into your code - and maybe rust-analyzer could apply it with a code action.

---

<div class="post-metadata">

**Author:** ![zackw](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/zackw/32/2071_2.png) [@zackw](https://internals.rust-lang.org/u/zackw)\
**Post date:** [March 3, 2025, 6:18pm UTC](https://internals.rust-lang.org/t/can-the-lifetimes-be-simpler/22493/16 "2025-03-03T18:18:16Z")

</div>

> [@dlight](#):
>
> Note that while simpler and easier to understand (IMO), this syntax is not the same as inferring the lifetimes: you still need to explicitly state which parameters exactly the output can borrow.

... and I would like to point out that when you are reading the documentation of a public API, "which parameters exactly the output can borrow" is important information that you need to know, _and you don't get to look at the body of the function to figure it out_.

(Often you _can_, but only by clicking through the `[source]` link; and the author of the API might not have chosen to publish the source; and it can be substantially more difficult than just reading the annotations!)

When people are saying things like

> [@quinedot](#):
>
> Not being able to tell what the lifetime relationships are based on the function signature is a _deteriment_ to any Rust developer other than the developer actively writing the function

this is what they mean.

---

<div class="post-metadata">

**Author:** ![scottmcm](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/scottmcm/32/2355_2.png) [@scottmcm](https://internals.rust-lang.org/u/scottmcm)\
**Post date:** [March 3, 2025, 7:01pm UTC](https://internals.rust-lang.org/t/can-the-lifetimes-be-simpler/22493/17 "2025-03-03T19:01:08Z")

</div>

The biggest reason, IMHO, that inferring them from the body is bad is that I want to be able to write

```rust
fn longest<'a>(s1: &'a str, s2: &'a str) -> &'a str {
    todo!()
}

```

Then use that in a bunch of other places, knowing that I won't suddenly have all the callers break once I actually implement it.

If `todo!()` bodies were treated as "well actually none of them matter so you can do whatever", that would be horrible.

---

<div class="post-metadata">

**Author:** ![quinedot](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/quinedot/32/7294_2.png) [@quinedot](https://internals.rust-lang.org/u/quinedot)\
**Post date:** [March 3, 2025, 8:03pm UTC](https://internals.rust-lang.org/t/can-the-lifetimes-be-simpler/22493/18 "2025-03-03T20:03:33Z")

</div>

> [@SparrowLii](#):
>
> According to this logic, the lifetimes should be like comments.

You must have misinterpreted my post, as making lifetimes optional is the opposite of my position.

---

<div class="post-metadata">

**Author:** ![dlight](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/dlight/32/8462_2.png) [@dlight](https://internals.rust-lang.org/u/dlight)\
**Post date:** [March 3, 2025, 9:49pm UTC](https://internals.rust-lang.org/t/can-the-lifetimes-be-simpler/22493/19 "2025-03-03T21:49:22Z")

</div>

Yeah, that's why such inference, if available for private functions, should be opt in and probably used sparingly.

---

<div class="post-metadata">

**Author:** ![scottmcm](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/scottmcm/32/2355_2.png) [@scottmcm](https://internals.rust-lang.org/u/scottmcm)\
**Post date:** [March 3, 2025, 10:10pm UTC](https://internals.rust-lang.org/t/can-the-lifetimes-be-simpler/22493/20 "2025-03-03T22:10:28Z")

</div>

> [@dlight](#):
>
> that's why such inference, if available for private functions

I think that for private functions like that, what people often want is not an inferred signature at all, but a "macro function" that essentially just copies the code into the caller. So it doesn't need to infer a trait type, but can just do `x + y` without worrying about what those types are until it's used. And where you can conditionally move something but the move checker runs in the context of the caller, etc.

[Next page](https://internals.rust-lang.org/t/can-the-lifetimes-be-simpler/22493.md?page=2)
