This question already has an answer here:
What does it mean for a type to have a static lifetime?
(1 answer)
Closed 10 months ago.
What is the proper understanding of the restriction T : 'static ?
As I understand it means "anything implementing T should not have a reference / pointer either directly or indirectly". I am asking because I used to think that it means anything implementing T should exist during the whole lifetime of the application, but such interpretation looks wrong for me now.
Your new understanding is correct. T: 'static means T does not contain lifetimes, or may live during the entire program, but doesn't have to.
See also Common Lifetime Misconceptions: if T: 'static then T must be valid for the entire program.
For example, String: 'static holds, but you can drop a String whenever you want and you cannot access it anymore. It is just that if I have a String and I don't drop it, I can hold it forever without have to worry it will be invalidated behind my back because, say, the underlying storage was freed.
Related
This question already has answers here:
What does the exclamation point mean in a trait implementation?
(3 answers)
Closed 9 months ago.
I am unable to locate the documentation for !Unpin referred to here in the docs.
More generally, the ! operator seem to lack corresponding documentation regarding traits. Specifically, it seems to represent Not as in Not Unpin or perhaps Not Unpinable in this case. I suppose it is different from Pin in some way otherwise it would be redundant. Currently, searching for the documentation is challenging since ! occurs so frequently otherwise.
It would be good if the operator behavior of ! on traits could be included in Appendix B: Operators and Symbols of the docs.
Unpin is one of several auto-traits, which are implemented automatically for any type that's compatible with it. And in the case of Unpin, that's, well, basically all of the types.
Auto-traits (and only auto-traits) can have negative implementations written by preceding the trait name with a !.
// By default, A implements Unpin
struct A {}
// But wait! Don't do that! I know something you don't, compiler.
impl !Unpin for A {}
Unpin, specifically, indicates to Rust that it is safe to move values of the implementing type T out of a Pin. Normally, Pin indicates that the thing inside shall not be moved. Unpin is the sort of opposite of that, which says "I know we just pinned this value, but I, as the writer of this type, know it's safe to move it anyway".
Generally, anything in Safe Rust is Unpin. The only time you'd want to mark something as !Unpin is if it's interfacing with something in another language like C. If you have a datatype that you're storing pointers to in C, for instance, then the C code may be written on the assumption that the data never changes addresses. In that case, you'd want to negate the Unpin implementation.
This question already has answers here:
Is this error due to the compiler's special knowledge about RefCell?
(1 answer)
How does the Rust compiler know `Cell` has internal mutability?
(3 answers)
When I can use either Cell or RefCell, which should I choose?
(3 answers)
Situations where Cell or RefCell is the best choice
(3 answers)
Closed 9 months ago.
The community reviewed whether to reopen this question 9 months ago and left it closed:
Original close reason(s) were not resolved
What are the exact differences between Cell, RefCell, and UnsafeCell?
RefCell
Let's start with RefCell, which in my personal experience is the most commonly used of the three.
Normally, in Rust, borrowing rules are checked at compile-time. You have to prove to the compiler (effectively, to a type-checker-like system called the borrow checker) that everything you're doing is fair game: that every object is either mutably borrowed once or immutably borrowed several times, and that those two threads don't cross. RefCell moves this check to runtime. You can convert a RefCell<T> to a &T with borrow and you can convert a RefCell<T> to &mut T with borrow_mut[1].
When you call these functions, at runtime, Rust checks that the usual borrow rules apply. You don't have to prove to the type checker that it works, but if it turns out that you're wrong, your program will panic[2]. We haven't changed the rules; you've just shoved them from compile time to runtime. This can be useful, for instance, if you're writing a recursive algorithm that manipulates data in some complicated way, and you've personally checked that the borrow rules are followed but you can't prove it to Rust.
Cell
Then there's Cell. A Cell is similar but it's not reference-based. Instead, the fundamental operation of a Cell is replace. replace takes a new value for the cell (by value) and, as the name implies, replaces the contents of the cell. No mutability checks need to be done in this case, since this operation happens in one fell swoop. You call the function, the function does its magic, and it returns. It's not giving you a reference back. There are other functions on Cell, like update in Nightly Rust, but they're all implemented in terms of replace.
Note that none of the types we're talking about can be shared across threads. So, in the case of Cell, there's no concern that one thread is trying to replace the value while another reads it. And in the case of RefCell, there's no need for a complicated locking mechanism to coordinate the runtime checks.
UnsafeCell
Enter UnsafeCell. UnsafeCell is RefCell with all of the safety checks removed. We haven't pushed safety off to the runtime; we've taken it in the backyard and shot it. The fundamental operation of UnsafeCell is get, which is like borrow_mut for RefCell except that it always returns a mutable pointer. It won't fail, it won't complain about race conditions or shared data, it will just give you a pointer. Note that *mut T is a raw pointer type, and the only way to modify a pointer of that type is with Unsafe Rust.
UnsafeCell should not be used directly. It's the compiler primitive used to implement the other two (safe) cell types. If you do decide that you need UnsafeCell for some reason, you need to be very careful to preserve compiler assumptions about data access, because it's very easy to make things go very wrong when you start dipping into Unsafe Rust. Trust me, I speak from experience on this. Last time I unsafe'd some of my code, it started causing enough problems that I eventually rewrote the whole thing using (safe) primitives and never actually figured out what was going wrong. It gets messy fast.
[1] You're actually converting a RefCell<T> to specialized types called Ref<'_, T> and RefMut<'_, T>, respectively, but those types are designed to act like ordinary references, so I omit that detail for brevity.
[2] There are variants of these functions called try_borrow and try_borrow_mut which return a Result rather than panicking on failure.
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?
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.
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.
This question already has answers here:
When is it useful to define multiple lifetimes in a struct?
(3 answers)
Closed 3 years ago.
I'm new to Rust, but I think I understand the concept of lifetime parameters well enough. I can see how this would be really useful for methods, when accepting multiple parameters by reference. However, I can't think of an example when a struct would hold references to two different variables with different lifetimes. The Rust book doesn't provide an example of a struct with multiple lifetimes.
The discussion around Elision 2.0 refers to "structs with a single lifetime parameter", and is focused on making it easier to work with types that have only one lifetime parameter, which suggests to me that this is the most common use case for lifetimes - either a struct has no lifetime, or it has one lifetime.
Can anyone provide me with a practical example of a struct with multiple lifetime parameters?
When you use a reference in a struct, you use a lifetime.
Sometimes you can just use the same lifetime for the various references of your struct, but you can't always do that.
A lifetime is a reference to the time a value is alive (between creation and deletion). And it's a constraint that the borrow checker will check. If you give the same lifetime to two fields, you add a constraint that often can't be verified, thus limiting the possible uses of your struct.
Here's a real world example:
pub struct DisplayableLine<'s, 'l, 'p> {
pub skin: &'s MadSkin,
pub line: &'p FmtLine<'l>,
pub width: Option<usize>, // available width
}
The struct itself is usually short lived. The skin and the line come from different origins. You could have a skin you just make for a display and that you want to immediately drop. Or a line you just made while you keep your skin around. Giving the same lifetime to both fields would be more limiting than necessary.
Of course when your struct is always referring to values that are created together and dropped together, having two lifetimes isn't necessary. But in practice this is rare.
I'm trying to wrap my head around Rust lifetimes (as the official guides don't really explain them that well).
Do rust lifetimes only refer to references, or can they refer to base/primitive values as well?
Lifetimes are the link between values and references to said values.
In order to understand this link, I will use a broken parallel: houses and addresses.
A house is a physical entity. It is built on a piece of land at some time, will live for a few dozen or hundred years, may be renovated multiple times during this time, and will most likely be destroyed at some point.
An address is a logical entity, it may point to a house, or to other physical entities (a field, a school, a train station, a company's HQ, ...).
The lifetime of a house is relatively clear: it represents the duration during which a house is usable, from the moment it is built to the moment it is destroyed. The house may undergo several renovations during this time, and what used to be a simple cabana may end up being a full-fledged manor, but that is of no concern to us; for our purpose the house is living throughout those transformations. Only its creation and ultimate destruction matter... even though it might be better if no one happen to be in the bedroom when we tear the roof down.
Now, imagine that you are a real estate agent. You do not keep the houses you sell in your office, it's impractical; you do, however, keep their addresses!
Without the notion of lifetime, from time to time your customers will complain because the address you sent them to... was the address of a garbage dump, and not at all that lovely two-story house you had the photography of. You might also get a couple of inquiries from the police station asking why people holding onto a booklet from your office were found in a just destroyed house, the ensuing lawsuit might shut down your business.
This is obviously a risk to your business, and therefore you should seek a better solution. What if each address could be tagged with the lifetime of the house it refers to, so that you know not to send people to their death (or disappointment) ?
You may have recognized the C manual memory management strategy in that garbage dump; in C it's up to you, the real estate agent developer, to make sure that your addresses (pointers/references) always refer to living houses.
In Rust, however, the references are tagged with a special marker: 'enough; it represents the a lower-bound on the lifetime of the value referred.
When the compiler checks whether your usage of the reference is safe or not, it asks the question:
Is the value still alive ?
It does not matter whether the value will be there for a 100 years afterward, as long as it lives long 'enough for the use you have of it.
No, they refer to values as well. If it is not clear from the context how long they will live, they have to be annotated as well. It is then called a lifetime bound.
In the following example it is necessary to specify that the value, the reference is referring to, lives at least as long as the reference itself:
use std::num::Primitive;
struct Foo<'a, T: Primitive + 'a> {
a: &'a T
}
Try deleting the + 'a and the compiler will complain. This is required since T could be anything implementing Primitive.
Yes, they only refer to references, however those references can refer to primitive types. Rust is not like Java (and similar languages) that make a distinction between primitive types, which are passed by value, and more complex types (Objects in Java) that are passed by reference. Complex types can be allocated on the stack and passed by value, and references can be taken to primitive types.
For example, here is a function that takes two references to i32's, and returns a reference to the larger one:
fn bigger<'a>(a: &'a i32, b: &'a i32) -> &'a i32 {
if a > b { a } else { b }
}
It uses the lifetime 'a to communicate that the lifetime of the returned reference is the same as that of the references passed in.
When you see a lifetime annotation (e.g. 'a) in the code, there's almost always a reference, or borrowed pointer, involved.
The full syntax for borrowed pointers is &'a T. 'a is the lifetime of the referent. T is the type of the referent.
Structs and enums can have lifetime parameters. This is usually a consequence of the struct or enum containing a borrowed pointer. When you store a borrowed pointer in a struct or enum, you must explicitly state the referent's lifetime. For example, the Cow enum in the standard library contains a borrowed pointer in one of its variants. Therefore, it has a lifetime parameter that is used in the borrowed pointer's type to define the referent's lifetime.
Traits can have type bounds and also a lifetime bound. The lifetime bound indicates the largest region in which all the borrowed pointers in a concrete implementation of that trait are valid (i.e. their referents are alive). If the implementation contains no borrowed
pointers, then the lifetime is inferred as 'static. Lifetime bounds can appear in type parameter definitions, in where clauses and on trait objects.
Sometimes, you might want to define a struct or enum with a lifetime parameter, but without a corresponding value to borrow from. You can use a marker type, such as ContravariantLifetime<'a>, to ensure the lifetime parameter has the proper variance (ContravariantLifetime corresponds to the variance of borrowed pointers; without a marker, the lifetime would be bivariant, which means the lifetime could be substituted with any other lifetime... not very useful!). See an example of this use case here.