5 ms·
Rust borrows must not exceed the lifetime associated with the borrow. By default that indeed means the borrow/reference cannot be moved into a context that outl
by mattashii 3y ago
Rust borrows must not exceed the lifetime associated with the borrow. By default that indeed means the borrow/reference cannot be moved into a context that outlives the function's execution, so it indeed can't be moved into a static global (unless `unsafe` is used explicitly)
- gpderetta 3y agoBut unsafe could be used inside the (rust equivalent) body of make and take_ownership without the compiler knowing. So would the rust compiler need to take this possibility into account when optimizing or not?
- johncolanduoni 3y agoNo, that would be undefined behavior, as would other transmutations you could do with unsafe like converting it to a mutable reference.
- gpderetta 3y agoWhat is UB in unsafe rust? You would be taking the addresses and then using the address of a variable that is still in scope. This assuming that rust has the same guaranteed elision as C++, extending the lifetime of an automatic object into the caller.
- kibwen 3y agoI believe the parent is saying "that would be undefined behavior" in answer to your question "would the rust compiler need to take this possibility into account". The compiler would not take this possibility into account, because the fact that references cannot outlive their referents is an invariant of the language that can only be broken by incorrect unsafe code. (To put it another way, in any context where the compiler would take this into account, it would do so regardless of the existence of the unsafe code, because unsafe code still ultimately has to uphold all the same invariants as safe code.)
- gpderetta 3y agoSure, but the reference doesn't outlive the referent in c++: the language guarantees that the local object in make is exactly the same object in the caller's caller. The question is whether rust gives this object identity guarantee as well and, if it does, whether there are other rules (like uniqueness of mutable refences) that still allows the optimisation.
- LegionMammal978 3y agoTransmuting lifetimes is one of the operations explicitly allowed for the std::mem::transmute() function [0]; lifetimes are only there to help safe code, and they have no effect on language-level UB. In fact, the 'static reference will be valid until the caller otherwise invalidates it. [0] https://doc.rust-lang.org/1.73.0/std/mem/fn.transmute.html#examples https://doc.rust-lang.org/1.73.0/std/mem/fn.transmute.html#e...
- kibwen 3y agoYou're allowed to extend lifetimes via transmute, but unsafe code must adhere to the invariants of references as laid out by the Rustonomicon: 1. A reference cannot outlive its referent 2. A mutable reference cannot be aliased https://doc.rust-lang.org/nomicon/references.html https://doc.rust-lang.org/nomicon/references.html Therefore, any use of transmute that extends a lifetime is UB if it causes the reference to outlive the referent. And because it's UB, the backend is going to assume the invariant holds universally and will optimize accordingly. The Nomicon also has a section on generating lifetimes via transmute, which it calls "unbounded lifetimes": https://doc.rust-lang.org/nomicon/unbounded-lifetimes.html https://doc.rust-lang.org/nomicon/unbounded-lifetimes.html
- LegionMammal978 3y ago> Therefore, any use of transmute that extends a lifetime is UB if it causes the reference to outlive the referent. By itself, a transmute of a currently-live reference cannot make it outlive its referent, in the sense used by the Rustonomicon. As it says later [0], a reference is alive until the point when it is last used, and no further. Thus, with unsafe code, the lifetime of a reference can extend past the point when it stops being alive. As a consequence, extending the lifetime can never cause UB by itself: only using the reference after it has been invalidated can cause language-level UB. Since language-level UB is based on liveness rather than lifetime, the compiler must assume that a reference passed to a function is valid even after it returns, up until the caller invalidates the reference by performing an operation incompatible with it remaining alive. [0] https://doc.rust-lang.org/nomicon/lifetimes.html#the-area-covered-by-a-lifetime https://doc.rust-lang.org/nomicon/lifetimes.html#the-area-co...
- kaba0 3y agoBut there could be an object with the same lifetime as the function passed to both f and h, in the article’s example — could they not collaborate still in safe Rust? Or does the compiler know about that?