Created
December 3, 2019 00:36
-
-
Save Arnavion/742feaa8a8b5ac1a23701c51e0d7530b to your computer and use it in GitHub Desktop.
launder-rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#![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