Skip to content

Instantly share code, notes, and snippets.

@Arnavion
Created December 3, 2019 00:36
Show Gist options
  • Save Arnavion/742feaa8a8b5ac1a23701c51e0d7530b to your computer and use it in GitHub Desktop.
Save Arnavion/742feaa8a8b5ac1a23701c51e0d7530b to your computer and use it in GitHub Desktop.
launder-rs
#![deny(rust_2018_idioms, warnings)]
#![deny(clippy::all, clippy::pedantic)]
//! This crate defines a `launder` macro that can be used to create a borrow that is scoped to the block where this macro is invoked,
//! even if the expression to create the borrow returns a `'static` borrow.
/// This macro can be used to create a borrow that is scoped to the block where this macro is invoked,
/// even if the expression to create the borrow returns a `'static` borrow.
///
/// For example, dereferencing a raw pointer with `&*` creates a borrow with an arbitrary lifetime which can be `'static`.
/// Dereferencing it using this macro will instead create a scoped borrow.
///
/// This is useful when you're doing further operations with the resulting `&` borrow,
/// and you'd like the compiler to protect you from using it outside of the scope where it was created.
///
/// If `expr` results in a `&mut` borrow instead, use `let_scoped_mut`.
///
/// # Example
///
/// This compiles:
///
/// ```rust
/// #[repr(C)]
/// struct S { inner: i32 }
///
/// unsafe fn foo(s: *const S) {
/// playground::let_scoped!(s = &*s);
///
/// requires_any(s); // *Does* compile
/// }
///
/// fn requires_any(_: &'_ S) { }
/// ```
///
/// ... but this does not:
///
/// ```rust,compile_fail
/// #[repr(C)]
/// struct S { inner: i32 }
///
/// unsafe fn foo(s: *const S) {
/// // Equivalent to `let s = &*s;` except that the reference is only valid for this scope, not for 'static.
/// playground::let_scoped!(s = &*s);
///
/// requires_static(s); // Fails to compile because `s` is not `'static`
/// }
///
/// fn requires_static(_: &'static S) { }
/// ```
///
/// ... which demonstrates that the borrow is not `'static`.
///
/// Compare with the code when not using this macro:
///
/// ```rust
/// #[repr(C)]
/// struct S { inner: i32 }
///
/// unsafe fn foo(s: *const S) {
/// let s = &*s;
///
/// requires_static(s); // *Does* compile
/// }
///
/// fn requires_static(_: &'static S) { }
/// ```
#[macro_export]
macro_rules! let_scoped {
($ident:ident $(: $ty:ty)? = $expr:expr) => {
// Need to call `_launder_scope()` rather than just construct a `&()` inline
// because the latter can become `&'static ()` due to const value promotion, defeating the purpose.
let scope = &$crate::_launder_scope();
let $ident $(: $ty)? = $crate::_launder(scope, $expr);
};
}
/// Same as `let_scoped`, but for `&mut` borrows.
#[macro_export]
macro_rules! let_scoped_mut {
($ident:ident $(: $ty:ty)? = $expr:expr) => {
// Need to call `_launder_scope()` rather than just construct a `&()` inline
// because the latter can become `&'static ()` due to const value promotion, defeating the purpose.
let scope = &$crate::_launder_scope();
let $ident $(: $ty)? = $crate::_launder_mut(scope, $expr);
};
}
/// Helper function used by the [`let_scoped`] macro. Do not call this directly.
///
/// Used to generate a `()` that can be borrowed for the scope where this function was invoked.
#[doc(hidden)]
pub fn _launder_scope() { }
/// Helper function used by the [`let_scoped`] macro. Do not call this directly.
///
/// Coerces the second parameter's lifetime to that of the first.
#[doc(hidden)]
pub fn _launder<'a, T>(_: &'a (), l: &'a T) -> &'a T { l }
/// Helper function used by the [`let_scoped`] macro. Do not call this directly.
///
/// Coerces the second parameter's lifetime to that of the first.
#[doc(hidden)]
pub fn _launder_mut<'a, T>(_: &'a (), l: &'a mut T) -> &'a mut T { l }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment