Rental (docs) is a crate designed to handle generalized cases where a struct needs to borrow its own contents. This can arise as an implementation detail in an API that you don't want to expose to your users by making them handle the borrowed lifetime. Instead, you can just present a single, opaque, owned "thing" that they can move around as normal. To achieve this, the rental
macro is used to define new types that represent pairs of an owner and a borrow associated with that owner that can then be passed around as a single unit. Naturally this is very unsafe by nature, so great care is taken to make sure no borrows can escape that will outlive the struct, or that short-lived data will be squirrelled into the struct.
One motivating case for me was being able to store a libloading Library
and the Symbol
s loaded from it in a single Api
struct. Under normal circumstances this would be impossible, but rental allows you to define a type that can do it safely (well, aside from the natural unsafety of calling symbols loaded from a dynamic library). Declaring that type looks as follows:
rental!{
mod rent_lib {
pub rental RentSym<'rental, S: ['rental]>(::std::sync::Arc<::libloading::Library>, ::libloading::Symbol<'rental, S>): Deref(S);
}
}
This defines a new struct that can hold a Library
, and a Symbol
loaded from it. The 'rental
lifetime is handled specially and represents the shared connection between the owner and the borrow.
To create an instance:
RentSym::try_new(lib, |l| unsafe { l.get(b"my_symbol") })
NOTE: The unsafe
here is solely because loading a symbol from the dylib is unsafe, unrelated to this library, I promise )
Inside this closure, the 'rental
lifetime becomes "existential" and cannot be satisfied by anything outside the closure, preventing anything tied to it from being exfiltrated. The Deref(S)
attached to the struct definition means that the struct will deref to the S
type of the Symbol
. This is safe only because the 'rental
lifetime does NOT appear in the type signature of S
. If it did, the derefed lifetime would be a lie and unsafety would ensue. The macro takes steps to ensure the type is compliant and will prevent Deref
from being impld if it fails. In such cases, the borrowed value can instead by accessed with the rent
method that gives you an existentially bound reference to the inner data that you can manipulate safely and return anything from (as long as it does not include the 'rental
lifetime).
This crate also provides predefined types for the simple case where you only need to store a borrowed reference along with its owner. However, because of the generalized way this crate produces the types, it can be slightly more awkward to use than owning_ref for that purpose, particularly when it comes to converting from one rental to another, which can be a bit verbose because of the workaround for the lack of HKT. This crate is instead mainly intended for when you have a complicated scenario and are willing to accept some boilerplate for safety.