When is a static lifetime not appropriate?
Asked Answered
C

2

9

I have found a lot of information across the web about rust lifetimes, including information about static lifetimes. It makes sense to me that, in certain situations, you must guarantee that a reference will outlive everything.

For instance, I have a reference that I’m passing to a thread, and the compiler is requesting that the reference been marked as static. In this scenario, that seems to make sense because the compiler can’t know how long the thread will live and thus needs to ensure the passed reference outlives the thread. (I think that’s correct?)

I don’t know where this comes from, but I am always concerned that marking something with a static lifetime is something to be skeptical of, and avoided when possible.

So I wonder if that’s correct. Should I be critical of marking things with a static lifetime? Are there situations when the compiler will want to require one, but an alternate strategy might actually be more optimal?

What are some concrete ways that I can reason about the application of a static lifetime, and possibly determine when it might not be appropriate?

Craftwork answered 6/3, 2021 at 20:45 Comment(1)
Commenting to link to The compiler suggests I add a 'static lifetime because the parameter type may not live long enough, but I don't think that's what I want which seems to exhibit a similar kind of uncertaintyAustronesia
S
24

As you might have already guessed, there is no definitive, technical answer to this.

As a newcomer to Rust, 'static references seem to defeat the entire purpose of the borrowing system and there is a notion to avoid them. Once you get more experienced, this notion will go away.

First of all, 'static is not bad as it seems, since all things that have no other lifetimes associated with them are 'static, e.g. String::new(). Notice that 'static does not mean that the value in question does truly live forever. It just means that the value can be made to live forever. In your threading-examples, the thread can't make any promises about its own lifetime, so it needs to be able to make all things passed to it live forever. Any owned value which does not include lifetimes shorter than 'static (like vec![1,2,3]) can be made to live forever (simply by not destroying them) and are therefor 'static.

Second, &'static - the static reference - does not come up often anyway. If it does, you'll usually be aware of why. You won't see a lot of fn foo(bar: &'static Bar) because there simply aren't that many use-cases for it, not because it is actively avoided.

There are situations where 'static does come up in surprising ways. Out of my head:

  • A Box<dyn Trait> is implicitly a Box<dyn Trait + 'static>. This is because when the type of the value inside the Box gets erased, it might have had lifetimes associated with it; and all (different) types must be valid for as long as the Box lives. Therefore all types need to share a common denominator wrt their lifetimes and Rust is defined to choose 'static. This choice is usually ok, but can lead to surprising "requires 'static" errors. You can generalize this explicitly to Box<dyn Trait + 'a>
  • If you have a custom impl Drop on your type, the Drop-checker may not be able to prove that the destructor is unable to observe values that have already been dropped. To prevent the Drop impl from accessing references to values that have already been dropped, the compiler requires the entire type to only have 'static references inside of it. This can be overcome by an unsafe impl, which lifts the 'static-requirement.
Scrod answered 6/3, 2021 at 22:32 Comment(2)
To add to this great answer, I'd like to draw attention to the difference between &'static T from T: static - the two are often confused by beginners, but they're not the same thing. In addition to the explanation in this answer, it's covered in depth by this section of the Rust lifetime misconceptions article by pretzelhammer, a regular on #rust.Darton
Oh my god. That Box<dyn Trait + 'a> saved my day. Thanks for the explanation!Sphacelus
C
1

Instead of &'static T, pass Arc<T> to the thread. This has only a tiny cost and ensures lifetimes will not be longer than necessary.

Cleora answered 6/3, 2021 at 22:32 Comment(1)
this should be a comment since it addresses an example presented alongside the question rather than the question itselfTimberlake

© 2022 - 2024 — McMap. All rights reserved.